UNPKG

metalsmith-hyphenate

Version:
158 lines (133 loc) 3.96 kB
'use strict'; var debug = require('debug')('metalsmith-hyphenate'); var extname = require('path').extname; var p5 = require('parse5'); var Hypher = require('hypher'); var minimatch = require('minimatch'); /** * Default Elements to hyphenate */ var DEFAULT_ELEMENTS = ['p', 'li', 'ol']; /** * Data attribute name */ var HYPHENATE_DATA_ATTR = 'data-hyphenate'; /** * A Metalsmith plugin to add soft hyphens in HTML * * @param {Object} options (optional) * @property {Array} [elements=['p', 'li', 'ol']] - HTML elements which needs to be hyphenated * @property {Array} [langModule='hyphenation.en-us'] - Hypher language module(pattern) to use * @property {String|Array} [ignore] - Glob expressions to ignore a file, or a set of files * @return {Function} */ function plugin(options) { options = options || {}; options.elements = options.elements || DEFAULT_ELEMENTS; options.langModule = options.langModule || 'hyphenation.en-us'; if ((options.ignore !== undefined) && (Object.prototype.toString.call(options.ignore) === '[object String]')) { options.ignore = [options.ignore]; } debug('File ignore glob expressions: %j', options.ignore); try { var hypher = new Hypher(require(options.langModule)); } catch (err) { console.log("Language module %s is not installed. Try 'npm install %s'", options.langModule, options.langModule); } /** * Check if a `value` is present in the array. * * @param {array} arr * @param value * @return {Boolean} */ function isPresent(arr, value) { return arr.indexOf(value) < 0 ? false : true; } /** * Check if a `file` is HTML. * * @param {String} file * @return {Boolean} */ function isHtml(file) { return /\.html?/.test(extname(file)); } /** * Traverses throught parsed DOM, and hyphenate text nodes * * @param {String} domString * @return {String} */ function hyphenateText(dom, forceHyphenateAllChildren) { if (dom.childNodes !== undefined) { dom.childNodes.forEach(function(node) { if (node.nodeName === '#text' && !isSkippable(node) && (forceHyphenateAllChildren || isPresent(options.elements, dom.tagName))) { node.value = hypher.hyphenateText(node.value); } else { hyphenateText(node, isPresent(options.elements, dom.tagName)); } }); } return dom; } /** * Check if the node is marked for skip * * @param {String} DOM node * @return {Boolean} */ function isSkippable(node) { var result = false; if (node && node.parentNode && node.parentNode.attrs && Array.isArray(node.parentNode.attrs)) { node.parentNode.attrs.forEach(function(attr) { if (attr.name && attr.value && attr.name === HYPHENATE_DATA_ATTR && attr.value === 'false') { result = true; } }); } return result; } /** * Check if the file matches the ignore glob patterns * * @param {String} file * @return {Boolean} */ function isIgnoredFile(file) { if (options.ignore !== undefined) { var result = false; options.ignore.forEach(function(pattern) { if (minimatch(file, pattern)) { result = true; return false; } }); return result; } return false; } return function(files, metalsmith, done) { setImmediate(done); Object.keys(files).forEach(function(file) { debug('checking if file matches ignore patterns: %s', file); if (isIgnoredFile(file) === true) { return; } debug('checking if it is an HTML file: %s', file); if (isHtml(file) === false) { return; } debug('hyphenating "%s"', file); var dom = p5.parse(files[file].contents.toString()); dom = hyphenateText(dom); files[file].contents = new Buffer(p5.serialize(dom)); }); }; } /** * Expose `plugin`. */ module.exports = plugin;