PostHTML
PostHTML is a tool for transforming HTML/XML with JS plugins. PostHTML itself is very small. It includes only a HTML parser, a HTML node tree API and a node tree stringifier.
All HTML transformations are made by plugins. And these plugins are just small plain JS functions, which receive a HTML node tree, transform it, and return a modified tree.
Usage
Install PostHTML
npm install --save-dev posthtml
Simple example
var posthtml = require('posthtml');
var html = '<myComponent><myTitle>Super Title</myTitle><myText>Awesome Text</myText></myComponent>';
posthtml()
.use(require('posthtml-custom-elements')())
.process(html)
.then(function(result) {
console.log(result.html);
});
Сomplex example
var posthtml = require('posthtml');
var html = '<html><body><p class="wow">OMG</p></body></html>';
posthtml([
require('posthtml-to-svg-tags')(),
require('posthtml-extend-attrs')({
attrsTree: {
'.wow' : {
id: 'wow_id',
fill: '#4A83B4',
'fill-rule': 'evenodd',
'font-family': 'Verdana'
}
}
})
])
.process(html)
.then(function(result) {
console.log(result.html);
});
Gulp plugin for PostHTML
npm install --save-dev gulp-posthtml
gulp.task('html', function() {
var posthtml = require('gulp-posthtml');
return gulp.src('src/**/*.html')
.pipe(posthtml([ require('posthtml-custom-elements')() ]))
.pipe(gulp.dest('build/'));
});
Check project-stub example with Gulp
Grunt plugin for PostHTML
npm install --save-dev grunt-posthtml
posthtml: {
options: {
use: [
require('posthtml-head-elements')({
headElements: 'test/config/head.json'
}),
require('posthtml-doctype')({
doctype: 'HTML 5'
}),
require('posthtml-include')({
encoding: 'utf-8'
})
]
},
build: {
files: [{
expand: true,
dot: true,
cwd: 'test/html/',
src: ['*.html'],
dest: 'test/tmp/'
}]
}
}
PostHTML with Jade engine in Expressjs
Also it's work with other view engine. Callback in app.engine
is called by res.render()
to render the template code.
app.engine('jade', function (path, options, callback) {
var plugins = [
require('posthtml-bem')(),
require('posthtml-textr')({ locale: 'ru'}, [
require('typographic-ellipses'),
require('typographic-single-spaces'),
require('typographic-quotes')
])
];
var html = require('jade').renderFile(path, options);
posthtml(plugins)
.process(html)
.then(function (result) {
if (typeof callback === 'function') {
var res;
try {
res = result.html;
} catch (ex) {
return callback(ex);
}
return callback(null, res);
}
});
})
app.set('view engine', 'jade');
CLI
Plugins
Take a look at the searchable catalog of the PostHTML plugins.
Ideas for plugins
Something more? ;)
Helpers
Dependency
PostHTML JSON tree example
input HTML
<a class="animals" href="#">
<span class="animals__cat" style="background: url(cat.png)">Cat</span>
</a>
Tree in PostHTML (PostHTMLTree)
[{
tag: 'a',
attrs: {
class: 'animals',
href: '#'
},
content: [
'\n ',
{
tag: 'span',
attrs: {
class: 'animals__cat',
style: 'background: url(cat.png)'
},
content: ['Cat']
},
'\n'
]
}]
Create PostHTML plugin
This is a simple function with a single argument.
Use posthtml-plugin-boilerplate boilerplate for create new plugin.
Synchronous plugin example
module.exports = function pluginName(tree) {
tree.match({ tag: 'img' }, function(node) {
node = Object.assign(node, { attrs: { class: 'img-wrapped' } });
return {
tag: 'span',
attrs: { class: 'img-wrapper' },
content: node
}
});
};
Classic asynchronous plugin example
var request = request('request');
module.exports = function pluginName(tree, cb) {
var tasks = 0;
tree.match({ tag: 'a' }, function(node) {
if (!/^(https?:)?\/\//.test(node.attrs.href)) {
return node;
}
request.head(node.attrs.href, function (err, resp) {
if (err) return done();
if (resp.statusCode >= 400) {
node.attrs.class += ' ' + 'Erroric';
}
if (resp.headers.contentType) {
node.attrs.class += ' content-type_' + resp.headers.contentType;
}
done();
});
tasks += 1;
return node;
});
function done() {
tasks -= 1;
if (!tasks) cb(null, tree);
}
};
Promised asynchronous plugin example
import parser from 'posthtml-parser';
import request from 'request';
export default tree => {
return new Promise(resolve => {
tree.match({ tag: 'user-info' }, (node) => {
request(`/api/user-info?${node.attrs.dataUserId}`, (err, resp, body) {
if (!err && body) node.content = parser(body);
resolve(tree);
});
});
});
};
class PostHTML
.use()
Arguments: {Function} plugin
Adds a plugin into the flow.
Example
var posthtml = require('posthtml');
var ph = posthtml()
.use(function(tree) {
return { tag: 'div', content: tree };
});
.process()
Arguments: {String|PostHTMLTree} html[, {Object} options]
Applies all plugins to the incoming html
object.
Returns: {{tree: PostHTMLTree, html: String}}
(eventually) an Object with modified html and/or tree.
Example
var ph = posthtml()
.process('<div></div>');
Options
singleTags
Array tags for extend default list single tags
Default: []
Options { singleTags: ['rect', 'custom'] }
...
<div>
...
<rect>
<custom>
</div>
closingSingleTag
Option to specify version closing single tags.
Accepts values: default
, slash
, tag
.
Default: default
Options { closingSingleTag: 'default' }
<singletag>
Options { closingSingleTag: 'slash' }
<singletag />
Options { closingSingleTag: 'tag' }
<singletag></singletag>
skipParse
Skips input html parsing process.
Default: null
posthtml()
.use(function(tree) { tree.tag = 'section'; })
.process({ tag: 'div' }, { skipParse: true })
.then(function (result) {
result.tree;
result.html;
});
sync
Try to run plugins synchronously. Throws if some plugins are async.
Default: null
posthtml()
.use(function(tree) { tree.tag = 'section'; })
.process('<div>foo</div>', { sync: true })
.html;
class API
.walk()
Arguments: {function(PostHTMLNode|String): PostHTMLNode|String}
Walk for all nodes in tree, run callback.
Example
tree.walk(function(node) {
let classes = node.attrs && node.attrs.class.split(' ') || [];
if(classes.includes(className)) {
return node;
}
return node;
});
.match()
Arguments: {Object|String|RegExp}, {function(PostHTMLNode|String): PostHTMLNode|String}
Find subtree in tree, run callback.
Example
tree.match({ tag: 'custom-tag' }, function(node) {
return Object.assign(node, {
tag: 'div',
attrs: { class: node.tag }
});
});
Support Array matchers
Example
tree.match([{ tag: 'b' }, { tag: 'strong' }], function(node) {
var style = 'font-weight: bold;';
node.tag = 'span';
node.attrs ? (
node.attrs.style ? (
node.attrs.style += style
) : node.attrs.style = style;
) : node.attrs = { style: style };
return node
});
License
MIT