grunt-processhtml
Version:
Process html files at build time to modify them depending on the release environment
605 lines (452 loc) • 15.9 kB
Markdown
# grunt-processhtml
[](https://www.npmjs.com/package/grunt-processhtml)
[](https://travis-ci.org/dciccale/grunt-processhtml)
[](https://www.npmjs.com/package/grunt-processhtml)
[](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
<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
<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
<!-- 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
<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