diff --git a/index.js b/index.js index df92628..22476fb 100644 --- a/index.js +++ b/index.js @@ -1,55 +1,55 @@ /* global hexo */ -var assign = require('object-assign'); +var assign = require('object-assign') -if (true === hexo.config.neat_enable) { - // HTML minifier - hexo.config.neat_html = assign({ - enable: true, - logger: true, - exclude: [], - ignoreCustomComments: [/^\s*more/], - removeComments: true, - removeCommentsFromCDATA: true, - collapseWhitespace: true, - collapseBooleanAttributes: true, - removeEmptyAttributes: true, - minifyJS: true, - minifyCSS: true, - }, hexo.config.neat_html); +if (hexo.config.neat_enable === true) { + // HTML minifier + hexo.config.neat_html = assign({ + enable: true, + logger: true, + exclude: [], + ignoreCustomComments: [/^\s*more/], + removeComments: true, + removeCommentsFromCDATA: true, + collapseWhitespace: true, + collapseBooleanAttributes: true, + removeEmptyAttributes: true, + minifyJS: true, + minifyCSS: true + }, hexo.config.neat_html) - // Css minifier - hexo.config.neat_css = assign({ - enable: true, - logger: true, - exclude: ['*.min.css'] - }, hexo.config.neat_css); + // Css minifier + hexo.config.neat_css = assign({ + enable: true, + logger: true, + exclude: ['*.min.css'] + }, hexo.config.neat_css) - // Js minifier - hexo.config.neat_js = assign({ - enable: true, - mangle: true, - logger: true, - output: {}, - compress: {}, - exclude: ['*.min.js'] - }, hexo.config.neat_js); + // Js minifier + hexo.config.neat_js = assign({ + enable: true, + mangle: true, + logger: true, + output: {}, + compress: {}, + exclude: ['*.min.js'] + }, hexo.config.neat_js) - // html, css, js compression - hexo.config.neat_gzip = assign({ - enable: true, - logger: true, - }, hexo.config.neat_gzip); + // html, css, js compression + hexo.config.neat_gzip = assign({ + enable: true, + logger: true + }, hexo.config.neat_gzip) - // html, css, js compression - hexo.config.neat_brotli = assign({ - enable: true, - logger: true, - }, hexo.config.neat_brotli); + // html, css, js compression + hexo.config.neat_brotli = assign({ + enable: true, + logger: true + }, hexo.config.neat_brotli) - var filter = require('./lib/filter'); - hexo.extend.filter.register('after_render:html', filter.logic_html); - hexo.extend.filter.register('after_render:css', filter.logic_css); - hexo.extend.filter.register('after_render:js', filter.logic_js); - hexo.extend.filter.register('after_generate', filter.logic_gzip); - hexo.extend.filter.register('after_generate', filter.logic_brotli); + var filter = require('./lib/filter') + hexo.extend.filter.register('after_render:html', filter.logicHtml) + hexo.extend.filter.register('after_render:css', filter.logicCss) + hexo.extend.filter.register('after_render:js', filter.logicJs) + hexo.extend.filter.register('after_generate', filter.logicGzip) + hexo.extend.filter.register('after_generate', filter.logicBrotli) } diff --git a/lib/filter.js b/lib/filter.js index 1456a22..93abf19 100644 --- a/lib/filter.js +++ b/lib/filter.js @@ -1,187 +1,193 @@ /* global hexo */ -'use strict'; -var CleanCSS = require('clean-css'), - UglifyJS = require('uglify-js'), - Htmlminifier = require('html-minifier').minify, - streamToArray = require('stream-to-array'); -var Promise = require('bluebird'); -var minimatch = require('minimatch'); -var zlib = require('zlib'); -var br = require('iltorb'); +'use strict' +var CleanCSS = require('clean-css') -function logic_html(str, data) { - var hexo = this, - options = hexo.config.neat_html; - // Return if disabled. - if (false === options.enable) return; +var UglifyJS = require('uglify-js') - var path = data.path; - var exclude = options.exclude; - if (exclude && !Array.isArray(exclude)) exclude = [exclude]; +var Htmlminifier = require('html-minifier').minify - if (path && exclude && exclude.length) { - for (var i = 0, len = exclude.length; i < len; i++) { - if (minimatch(path, exclude[i], {matchBase: true})) return str; - } +var streamToArray = require('stream-to-array') +var Promise = require('bluebird') +var minimatch = require('minimatch') +var zlib = require('zlib') +var br = require('iltorb') + +function logicHtml (str, data) { + var hexo = this + + var options = hexo.config.neat_html + // Return if disabled. + if (options.enable === false) return + + var path = data.path + var exclude = options.exclude + if (exclude && !Array.isArray(exclude)) exclude = [exclude] + + if (path && exclude && exclude.length) { + for (var i = 0, len = exclude.length; i < len; i++) { + if (minimatch(path, exclude[i], { matchBase: true })) return str } + } - var result = Htmlminifier(str, options); - var saved = ((str.length - result.length) / str.length * 100).toFixed(2); - if (options.logger) { - var log = hexo.log || console.log; - log.log('Minify the html: %s [%s saved]', path, saved + '%'); - } - return result; + var result = Htmlminifier(str, options) + var saved = ((str.length - result.length) / str.length * 100).toFixed(2) + if (options.logger) { + var log = hexo.log || console.log + log.log('Minify the html: %s [%s saved]', path, saved + '%') + } + return result } -function logic_css(str, data) { - var hexo = this, - options = hexo.config.neat_css; - // Return if disabled. - if (false === options.enable) return; +function logicCss (str, data) { + var hexo = this - var path = data.path; - var exclude = options.exclude; - if (exclude && !Array.isArray(exclude)) exclude = [exclude]; + var options = hexo.config.neat_css + // Return if disabled. + if (options.enable === false) return - if (path && exclude && exclude.length) { - for (var i = 0, len = exclude.length; i < len; i++) { - if (minimatch(path, exclude[i], {matchBase: true})) return str; - } + var path = data.path + var exclude = options.exclude + if (exclude && !Array.isArray(exclude)) exclude = [exclude] + + if (path && exclude && exclude.length) { + for (var i = 0, len = exclude.length; i < len; i++) { + if (minimatch(path, exclude[i], { matchBase: true })) return str } + } - return new Promise(function (resolve, reject) { - new CleanCSS({level:2}).minify(str, function (err, result) { - if (err) return reject(err); - var saved = ((str.length - result.styles.length) / str.length * 100).toFixed(2); - resolve(result.styles); - if (options.logger) { - var log = hexo.log || console.log; - log.log('Minify the css: %s [%s saved]', path, saved + '%'); + return new Promise(function (resolve, reject) { + new CleanCSS({ level: 2 }).minify(str, function (err, result) { + if (err) return reject(err) + var saved = ((str.length - result.styles.length) / str.length * 100).toFixed(2) + resolve(result.styles) + if (options.logger) { + var log = hexo.log || console.log + log.log('Minify the css: %s [%s saved]', path, saved + '%') + } + }) + }) +} + +function logicJs (str, data) { + var hexo = this + + var options = hexo.config.neat_js + // Return if disabled. + if (options.enable === false) return + + var path = data.path + var exclude = options.exclude + if (exclude && !Array.isArray(exclude)) exclude = [exclude] + + if (path && exclude && exclude.length) { + for (var i = 0, len = exclude.length; i < len; i++) { + if (minimatch(path, exclude[i], { matchBase: true })) return str + } + } + + // uglifyjs doesn't like unsupported options + delete options.enable + delete options.exclude + var jsLogger = options.logger + delete options.logger + + var result = UglifyJS.minify(str, options) + var saved = ((str.length - result.code.length) / str.length * 100).toFixed(2) + if (jsLogger) { + var log = hexo.log || console.log + log.log('Minify the js: %s [%s saved]', path, saved + '%') + } + return result.code +} + +function logicGzip () { + var hexo = this + + var options = hexo.config.neat_gzip + // Return if disabled. + if (options.enable === false) return + + const route = hexo.route + const routeList = route.list() + + return Promise.all(routeList.filter(path => (path.endsWith('.html') || path.endsWith('.js') || path.endsWith('.css'))).map(path => { + return new Promise((resolve, reject) => { + // Grab all assets using hexo router + const assetPath = route.get(path) + let assetTxt = '' + // Extract the content + assetPath.on('data', (chunk) => (assetTxt += chunk)) + assetPath.on('end', () => { + if (assetTxt.length) { + // gzip compress using highest level + zlib.gzip(assetTxt, { level: zlib.Z_BEST_COMPRESSION }, (err, Input) => { + if (!err) { + // Save the compressed file to .gz + route.set(path + '.gz', Input) + // Logging + var saved = ((assetTxt.length - Input.toString().length) / assetTxt.length * 100).toFixed(2) + if (options.logger) { + var log = hexo.log || console.log + log.log('Gzip-compressed %s [%s saved]', path, saved + '%') + } + resolve(assetTxt) + } else { + reject(err) } - }); - }); -} - -function logic_js(str, data) { - var hexo = this, - options = hexo.config.neat_js; - // Return if disabled. - if (false === options.enable) return; - - var path = data.path; - var exclude = options.exclude; - if (exclude && !Array.isArray(exclude)) exclude = [exclude]; - - if (path && exclude && exclude.length) { - for (var i = 0, len = exclude.length; i < len; i++) { - if (minimatch(path, exclude[i], {matchBase: true})) return str; + }) } - } - - //uglifyjs doesn't like unsupported options - delete options.enable; - delete options.exclude; - var js_logger = options.logger; - delete options.logger; - - var result = UglifyJS.minify(str, options); - var saved = ((str.length - result.code.length) / str.length * 100).toFixed(2); - if (js_logger) { - var log = hexo.log || console.log; - log.log('Minify the js: %s [%s saved]', path, saved + '%'); - } - return result.code; + }) + }) + })) } -function logic_gzip() { - var hexo = this, - options = hexo.config.neat_gzip; - // Return if disabled. - if (false === options.enable) return; +function logicBrotli () { + var hexo = this - var gz_logger = options.logger; - const route = hexo.route; - const routeList = route.list(); - - return Promise.all(routeList.filter(path => (path.endsWith('.html') || path.endsWith('.js') || path.endsWith('.css'))).map(path => { - return new Promise((resolve, reject) => { - // Grab all assets using hexo router - const assetPath = route.get(path); - let assetTxt = ''; - // Extract the content - assetPath.on('data', (chunk) => (assetTxt += chunk)); - assetPath.on('end', () => { - if (assetTxt.length) { - // gzip compress using highest level - zlib.gzip(assetTxt, {level:zlib.Z_BEST_COMPRESSION}, (err, buffer) => { - if (!err) { - // Save the compressed file to .gz - route.set(path + '.gz', buffer); - //Logging - var saved = ((assetTxt.length - buffer.toString().length) / assetTxt.length * 100).toFixed(2); - if (gz_logger) { - var log = hexo.log || console.log; - log.log('Gzip-compressed %s [%s saved]', path, saved + '%'); - } - resolve(assetTxt); - } else { - reject(err); + var options = hexo.config.neat_brotli + // Return if disabled. + if (options.enable === false) return + + const route = hexo.route + const routeList = route.list() + + return Promise.all(routeList.filter(path => (path.endsWith('.html') || path.endsWith('.js') || path.endsWith('.css'))).map(path => { + return new Promise((resolve, reject) => { + // Grab all assets using hexo router + const assetPath = route.get(path) + let assetTxt = '' + // Extract the content + assetPath.on('data', (chunk) => (assetTxt += chunk)) + assetPath.on('end', () => { + if (assetTxt.length) { + // Input has to be buffer for brotli + var input = new Buffer.from(assetTxt, 'utf-8') + // brotli compress using highest level + br.compress(input, { quality: br.BROTLI_MAX_QUALITY }, (err, output) => { + if (!err) { + // Save the compressed file to .br + route.set(path + '.br', output) + // Logging + var saved = ((input.length - output.toString().length) / input.length * 100).toFixed(2) + if (options.logger) { + var log = hexo.log || console.log + log.log('Brotli-compressed %s [%s saved]', path, saved + '%') } - }); - } - }); - }); - })); -} - -function logic_brotli() { - var hexo = this, - options = hexo.config.neat_brotli; - // Return if disabled. - if (false === options.enable) return; - - var br_logger = options.logger; - const route = hexo.route; - const routeList = route.list(); - - return Promise.all(routeList.filter(path => (path.endsWith('.html') || path.endsWith('.js') || path.endsWith('.css'))).map(path => { - return new Promise((resolve, reject) => { - // Grab all assets using hexo router - const assetPath = route.get(path); - let assetTxt = ''; - // Extract the content - assetPath.on('data', (chunk) => (assetTxt += chunk)); - assetPath.on('end', () => { - if (assetTxt.length) { - // Input has to be buffer for brotli - var buffer = new Buffer.from(assetTxt, "utf-8"); - // brotli compress using highest level - br.compress(buffer, {quality:br.BROTLI_MAX_QUALITY}, (err, output) => { - if (!err) { - // Save the compressed file to .br - route.set(path + '.br', output); - //Logging - var saved = ((buffer.length - output.toString().length) / buffer.length * 100).toFixed(2); - if (br_logger) { - var log = hexo.log || console.log; - log.log('Brotli-compressed %s [%s saved]', path, saved + '%'); - } - resolve(assetTxt); - } else { - reject(err); - } - }); - } - }); - }); - })); + resolve(assetTxt) + } else { + reject(err) + } + }) + } + }) + }) + })) } module.exports = { - logic_html: logic_html, - logic_css: logic_css, - logic_js: logic_js, - logic_gzip: logic_gzip, - logic_brotli: logic_brotli -}; + logicHtml: logicHtml, + logicCss: logicCss, + logicJs: logicJs, + logicGzip: logicGzip, + logicBrotli: logicBrotli +}