# grunt-processhtml

[![NPM version](https://img.shields.io/npm/v/grunt-processhtml.svg)](https://www.npmjs.com/package/grunt-processhtml)
[![Build Status](https://api.travis-ci.org/dciccale/grunt-processhtml.svg)](https://travis-ci.org/dciccale/grunt-processhtml)
[![NPM downloads](https://img.shields.io/npm/dm/grunt-processhtml.svg)](https://www.npmjs.com/package/grunt-processhtml)

[![Join the chat at https://gitter.im/dciccale/grunt-processhtml](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/dciccale/grunt-processhtml?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)

> Process html files at build time to modify them depending on the release environment

### Looking for the stand-alone version?

Use [node-htmlprocessor](https://github.com/dciccale/node-htmlprocessor)

## Getting Started
This plugin requires Grunt `^1.0.1`

If you haven't used [Grunt](http://gruntjs.com/) before, be sure to check out the [Getting Started](http://gruntjs.com/getting-started) guide, as it explains how to create a [Gruntfile](http://gruntjs.com/sample-gruntfile) as well as install and use Grunt plugins. Once you're familiar with that process, you may install this plugin with this command:

```shell
npm install grunt-processhtml --save-dev
```

Once the plugin has been installed, it may be enabled inside your Gruntfile with this line of JavaScript:

```js
grunt.loadNpmTasks('grunt-processhtml');
```

## The "processhtml" task

Process `html` files with special comments:

```html
<!-- build:<type>[:target] [inline] [value] -->
...
<!-- /build -->
```

##### type
This is required.

Types: `js`, `css`, `remove`, `template`, `include` or any html attribute if written like this: `[href]`, `[src]`, etc.

##### target
This is optional.

Is the target name of your grunt task, for example: `dist`. Is supported for all types, so you can always specify the target if needed.

You can pass multiple comma-separated targets, e.g. `<!-- build:remove:dist,dev,prod -->` and block will be parsed for each.

##### inline
This modifier can be used with `js` and `css` types.

If used, the styles or scripts will be included in the output html file.

##### value
Required for types: `js`, `css`, `include` and `[attr]`.

Optional for types: `remove`, `template`, `js` and `css` types with `inline` modifier.

Could be a file name: `script.min.js` or a path if an attribute like `[src]` is specified to keep the original file name intact but replace its path.

### Simple examples

##### `build:js[:targets] [inline] <value>`

Replace many script tags into one.

`[:targets]` Optional build targets.

`inline` Optional modifier.

`<value>` Required value: A file path.

```html
<!-- build:js app.min.js -->
<script src="my/lib/path/lib.js"></script>
<script src="my/deep/development/path/script.js"></script>
<!-- /build -->

<!-- changed to -->
<script src="app.min.js"></script>
```

You can embed your javascript:

```html
<!-- build:js inline app.min.js -->
<script src="my/lib/path/lib.js"></script>
<script src="my/deep/development/path/script.js"></script>
<!-- /build -->

<!-- changed to -->
<script>
  // app.min.js code here
</script>
```

or

```html
<!-- build:js inline -->
<script src="my/lib/path/lib.js"></script>
<script src="my/deep/development/path/script.js"></script>
<!-- /build -->

<!-- changed to -->
<script>
  // my/lib/path/lib.js code here then...
  // my/deep/development/path/script.js code goes here
</script>
```

##### `build:css[:targets] [inline] <value>`

Replace many stylesheet link tags into one.

`[:targets]` Optional build targets.

`inline` Optional modifier.

`<value>` Required value: A file path.

```html
<!-- build:css style.min.css -->
<link rel="stylesheet" href="path/to/normalize.css">
<link rel="stylesheet" href="path/to/main.css">
<!-- /build -->

<!-- changed to -->
<link rel="stylesheet" href="style.min.css">
```

You can embed your styles like with `js` type above:

```html
<!-- build:css inline -->
<link rel="stylesheet" href="path/to/normalize.css">
<link rel="stylesheet" href="path/to/main.css">
<!-- /build -->

<!-- changed to -->
<style>
  /* path/to/normalize.css */
  /* path/to/main.css */
</style>
```

or

```html
<!-- build:css inline style.min.css -->
<link rel="stylesheet" href="path/to/normalize.css">
<link rel="stylesheet" href="path/to/main.css">
<!-- /build -->

<!-- changed to -->
<style>
  /* style.min.css */
</style>
```

##### `build:<[attr]>[:targets] <value>`

Change the value of an attribute. In most cases using `[src]` and `[href]` will be enough but it works with any html attribute.

`<[attr]>` Required html attribute, i.e. `[src]`, `[href]`.

`[:targets]` Optional build targets.

`<value>` Required value: A path, a file path or any string value

```html
<!-- If only a path is used, the original file name will remain -->

<!-- build:[src] js/ -->
<script src="my/lib/path/lib.js"></script>
<script src="my/deep/development/path/script.js"></script>
<!-- /build -->

<!-- changed the src attribute path -->
<script src="js/lib.js"></script>
<script src="js/script.js"></script>

<!-- build:[href] img/ -->
<link rel="apple-touch-icon-precomposed" href="skins/demo/img/icon.png">
<link rel="apple-touch-icon-precomposed" href="skins/demo/img/icon-72x72.png" sizes="72x72">
<!-- /build -->

<!-- changed the href attribute path -->
<link rel="apple-touch-icon-precomposed" href="img/icon.png">
<link rel="apple-touch-icon-precomposed" href="img/icon-72x72.png" sizes="72x72">

<!-- build:[class]:dist production -->
<html class="debug_mode">
<!-- /build -->

<!-- this will change the class to 'production' only when the 'dist' build is executed -->
<html class="production">

```

##### `build:include[:targets] <value>`

Include an external file.

`[:targets]` Optional build targets.

`<value>` Required value: A file path.

```html
<!-- build:include header.html -->
This will be replaced by the content of header.html
<!-- /build -->

<!-- build:include:dev dev/content.html -->
This will be replaced by the content of dev/content.html
<!-- /build -->

<!-- build:include:dist dist/content.html -->
This will be replaced by the content of dist/content.html
<!-- /build -->
```

##### `build:template[:targets]`

Process a template block with a data object inside [options.data](#optionsdata).

`[:targets]` Optional build targets.


```html
<!-- build:template
<p>Hello, <%= name %></p>
/build -->

<!--
notice that the template block is commented
to prevent breaking the html file and keeping it functional
-->
```

##### `build:remove[:targets]`

Remove a block.

`[:targets]` Optional build targets

```html
<!-- build:remove -->
<p>This will be removed when any processhtml target is done</p>
<!-- /build -->

<!-- build:remove:dist -->
<p>But this one only when doing processhtml:dist target</p>
<!-- /build -->
```

### Overview
In your project's Gruntfile, add a section named `processhtml` to the data object passed into `grunt.initConfig()`.

```js
grunt.initConfig({
  processhtml: {
    options: {
      // Task-specific options go here.
    },
    your_target: {
      // Target-specific file lists and/or options go here.
    },
  },
})
```

### Options

#### options.process
Type: `Boolean`
Default value: `false`

Process the entire `html` file through `grunt.template.process`, a default object with the build target will be passed to the
template in the form of `{environment: target}` where environment will be the build target of the grunt task.

*Important note: The `process` option is not needed if you don't want to process the entire html file. See the example
below to see that you can have templates blocks to be processed.*

If you do want to process the whole file as a template, it will be compiled after compiling the inside template blocks
if any.

#### options.environment
Type: `Object`
Default value: `target`

The environemnt variable will be available to use in the comments, it defaults to the task target.

#### options.data
Type: `Object`
Default value: `{}`

An object `data` that is passed to the `html` file used to compile all template blocks and the entire file if `process`
is true.

#### options.templateSettings
Type: `Object`
Default value: `null` (Will use default lodash template delimiters `<%` and `%>`)

Define the `templateSettings` option with lodash [templateSettings](http://lodash.com/docs#templateSettings) options to customize the
template syntax.

```javascript
templateSettings: {
  interpolate: /{{([\s\S]+?)}}/g // mustache
}
```

#### options.includeBase
Type: `String`
Default value: `null` (Will use the path of the including file)

Specify an optional path to look for include files. ie, `app/assets/includes/`

#### options.commentMarker
Type: `String`
Default value: `build`

Specify the word used to indicate the special begin/end comments.  This is useful if you want to use this plugin
in conjunction with other plugins that use a similar, conflicting `build:<type>` comment
(such as [grunt-usemin](https://github.com/yeoman/grunt-usemin)).

With `options.commentMarker` set to `process`, a typical comment would look like:

```html
<!-- process:<type>[:targets] [value] -->
...
<!-- /process -->
```

#### options.strip
Type: `Boolean`
Default value: `null`

Specifying `true` will strip comments which do not match the current target:

```javascript
strip: true
```

#### options.recursive
Type: `Boolean`
Default value: `false`

Recursively process files that are being included using `build:include`.

```javascript
recursive: true
```

#### options.customBlockTypes
Type: `Array`
Default value: `[]`

Define an array of `.js` files that define custom block types.

```javascript
customBlockTypes: ['custom-blocktype.js']
```

A custom block type example:

`custom-blocktype.js`

```js
'use strict';

module.exports = function (processor) {
  // This will allow to use this <!-- build:customBlock[:target] <value> --> syntax
  processor.registerBlockType('customBlock', function (content, block, blockLine, blockContent) {
    var title = '<h1>' + block.asset + '</h1>';
    var result = content.replace(blockLine, title);

    return result;
  });
};
```

`file.html`

```html
<!-- build:customBlock myValue -->
<p>This will be replaced with the result of the custom block above</p>
<!-- /build -->
```

The result will be

```html
<h1>myValue</h1>
```


### Usage Examples

#### Default Options
Set the task in your grunt file which is going to process the `index.html` file and save the output to
`dest/index.html`

```js
grunt.initConfig({
  processhtml: {
    options: {
      data: {
        message: 'Hello world!'
      }
    },
    dist: {
      files: {
        'dest/index.html': ['index.html']
      }
    }
  }
});
```

#### What will be processed?
Following the previous task configuration, the `index.html` could look like this:

```html
<!doctype html>
<title>title</title>

<!-- build:[href] img/ -->
<link rel="apple-touch-icon-precomposed" href="my/theme/img/apple-touch-icon-precomposed.png">
<link rel="apple-touch-icon-precomposed" href="my/theme/img/apple-touch-icon-72x72-precomposed.png" sizes="72x72">
<!-- /build -->

<!-- build:css style.min.css -->
<link rel="stylesheet" href="normalize.css">
<link rel="stylesheet" href="main.css">
<!-- /build -->

<!-- build:js app.min.js -->
<script src="js/libs/require.js" data-main="js/config.js"></script>
<!-- /build -->

<!-- build:include header.html -->
This will be replaced by the content of header.html
<!-- /build -->

<!-- build:template
<p><%= message %></p>
/build -->

<!-- build:remove -->
<p>This is the html file without being processed</p>
<!-- /build -->
```

After processing this file, the output will be:

```html
<!doctype html>
<title>title</title>

<link rel="apple-touch-icon-precomposed" href="img/apple-touch-icon-precomposed.png">
<link rel="apple-touch-icon-precomposed" href="img/apple-touch-icon-72x72-precomposed.png" sizes="72x72">

<link rel="stylesheet" href="style.min.css">

<script src="app.min.js"></script>

<h1>Content from header.html</h1>

<p>Hello world!</p>
```

#### Advanced example
In this example there are multiple targets where we can process the html file depending on which target is being run.

```js
grunt.initConfig({
  processhtml: {
    dev: {
      options: {
        data: {
          message: 'This is development environment'
        }
      },
      files: {
        'dev/index.html': ['index.html']
      }
    },
    dist: {
      options: {
        process: true,
        data: {
          title: 'My app',
          message: 'This is production distribution'
        }
      },
      files: {
        'dest/index.html': ['index.html']
      }
    },
    custom: {
      options: {
        templateSettings: {
          interpolate: /{{([\s\S]+?)}}/g // mustache
        },
        data: {
          message: 'This has custom template delimiters'
        }
      },
      files: {
        'custom/custom.html': ['custom.html']
      }
    }
  }
});
```

The `index.html` to be processed (the `custom.html` is below):

```html
<!doctype html>
<!-- notice that no special comment is used here, as process is true.
if you don't mind having <%= title %> as the title of your app
when not being processed; is a perfectly valid title string -->
<title><%= title %></title>

<!-- build:css style.min.css -->
<link rel="stylesheet" href="normalize.css">
<link rel="stylesheet" href="main.css">
<!-- /build -->

<!-- build:template
<p><%= message %></p>
/build -->

<!-- build:remove -->
<p>This is the html file without being processed</p>
<!-- /build -->

<!-- build:remove:dist -->
<script src="js/libs/require.js" data-main="js/config.js"></script>
<!-- /build -->

<!-- build:template
<% if (environment === 'dev') { %>
<script src="app.js"></script>
<% } else { %>
<script src="app.min.js"></script>
<% } %>
/build -->
```

The `custom.html` to be processed:

```html
<!doctype html>
<html>
  <head>
    <title>Custom template delimiters example</title>
  </head>

  <body>
    <!-- build:template
    {{ message }}
    /build -->
  </body>
</html>
```

## Contributing
In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using [Grunt](http://gruntjs.com/).

## Release History
- 0.4.1 node-htmlprocessor@0.2.4
- 0.4.0 Update Grunt to 1.0
- 0.3.13 node-htmlprocessor@0.2.3 and clone data object (#85)
- 0.3.12 Update [node-htmlprocessor](https://github.com/dciccale/node-htmlprocessor) to version 0.2.2 (escape regex from remove)
- 0.3.11 [get ready for grunt v1.0.0](https://github.com/gruntjs/grunt-docs/blob/master/Blog-2016-02-11-Grunt-1.0.0-rc1-released.md#peer-dependencies)
- 0.3.10 Update [node-htmlprocessor](https://github.com/dciccale/node-htmlprocessor) to version 0.2.1
- 0.3.9 Update [node-htmlprocessor](https://github.com/dciccale/node-htmlprocessor) to version 0.2.0
- 0.3.8 Fix #74
- 0.3.7 Update [node-htmlprocessor](https://github.com/dciccale/node-htmlprocessor) dependency with added `inline` modifier
- 0.3.6 Update node-htmlprocessor version and add specific test for templates
- 0.3.5 Fixes issue when passing data to a `template`
- 0.3.4 Fixes issue when passing a path te replace an `[attr]`
- 0.3.3 Add [node-htmlprocessor](https://github.com/dciccale/node-htmlprocessor) as a dependency
- 0.3.2 Fix/feature #39
- 0.3.1 Fix #35
- 0.3.0 Allow creating custom block types.
- 0.2.9 Added `recursive` option
- 0.2.8 Changed `include` to not use `replace()`
- 0.2.7 Added `commentMarker` option
- 0.2.6 Fix #14 and added grunt-release
- 0.2.5 Create first tag using grunt-release
- 0.2.3 Fix #8
- 0.2.2 Small code refactor
- 0.2.1 Added `templateSettings` option tu customize template delimiters
- 0.2.0 Added the `build:include` feature to include any external file
- 0.1.1 Lint js files inside tasks/lib/
- 0.1.0 Initial release
