mirror of https://github.com/curbengh/hexo-yam
feat: minify xml
This commit is contained in:
parent
d75b10b64b
commit
317b5e43d3
19
README.md
19
README.md
|
@ -21,6 +21,7 @@ Yet Another Minifier for Hexo. Minify and compress HTML, JS, CSS and SVG. XML, J
|
||||||
- [SVG](#svg)
|
- [SVG](#svg)
|
||||||
- [Gzip](#gzip)
|
- [Gzip](#gzip)
|
||||||
- [Brotli](#brotli)
|
- [Brotli](#brotli)
|
||||||
|
- [XML](#xml)
|
||||||
- [Globbing](#globbing)
|
- [Globbing](#globbing)
|
||||||
- [HTTP Compression](#http-compression)
|
- [HTTP Compression](#http-compression)
|
||||||
|
|
||||||
|
@ -191,6 +192,24 @@ minify:
|
||||||
- **globOptions** - See [globbing](#globbing) section.
|
- **globOptions** - See [globbing](#globbing) section.
|
||||||
- **level** - Compression level. Range `1-11`. Defaults to `11`, or the value of [`zlib.constants.BROTLI_MAX_QUALITY`](https://nodejs.org/docs/latest-v12.x/api/zlib.html#zlib_brotli_constants)
|
- **level** - Compression level. Range `1-11`. Defaults to `11`, or the value of [`zlib.constants.BROTLI_MAX_QUALITY`](https://nodejs.org/docs/latest-v12.x/api/zlib.html#zlib_brotli_constants)
|
||||||
|
|
||||||
|
## XML
|
||||||
|
|
||||||
|
``` yaml
|
||||||
|
minify:
|
||||||
|
xml:
|
||||||
|
enable: false
|
||||||
|
include:
|
||||||
|
- '*.xml'
|
||||||
|
- '!*.min.xml'
|
||||||
|
```
|
||||||
|
- **enable** - Enable the plugin. Defaults to `false`.
|
||||||
|
- **priority** - Plugin's priority. Defaults to `10`.
|
||||||
|
- **verbose** - Verbose output. Defaults to `false`.
|
||||||
|
- **include** - Include files. Support [wildcard](http://www.globtester.com/) pattern(s) in a string or array.
|
||||||
|
- Exclude `*.min.xml` by default.
|
||||||
|
- **removeComments** - Remove [comments](https://developer.mozilla.org/en-US/docs/Web/XML/XML_introduction) in xml. Defaults to `true`.
|
||||||
|
- **globOptions** - See [globbing](#globbing) section.
|
||||||
|
|
||||||
## Globbing
|
## Globbing
|
||||||
|
|
||||||
Use "globOptions" to customise how glob patterns match files. Refer to [micromatch](https://github.com/micromatch/micromatch#options) for available options.
|
Use "globOptions" to customise how glob patterns match files. Refer to [micromatch](https://github.com/micromatch/micromatch#options) for available options.
|
||||||
|
|
13
index.js
13
index.js
|
@ -59,6 +59,14 @@ const brotliDefault = {
|
||||||
include: ['*.html', '*.css', '*.js', '*.txt', '*.ttf', '*.atom', '*.stl', '*.xml', '*.svg', '*.eot', '*.json'],
|
include: ['*.html', '*.css', '*.js', '*.txt', '*.ttf', '*.atom', '*.stl', '*.xml', '*.svg', '*.eot', '*.json'],
|
||||||
globOptions: { basename: true }
|
globOptions: { basename: true }
|
||||||
}
|
}
|
||||||
|
const xmlDefault = {
|
||||||
|
enable: false,
|
||||||
|
priority: 10,
|
||||||
|
verbose: false,
|
||||||
|
include: ['*.xml', '!*.min.xml'],
|
||||||
|
removeComments: true,
|
||||||
|
globOptions: { basename: true }
|
||||||
|
}
|
||||||
|
|
||||||
hexo.config.minify = Object.assign(minifyDefault, hexo.config.minify)
|
hexo.config.minify = Object.assign(minifyDefault, hexo.config.minify)
|
||||||
hexo.config.minify.html = Object.assign(htmlDefault, hexo.config.minify.html)
|
hexo.config.minify.html = Object.assign(htmlDefault, hexo.config.minify.html)
|
||||||
|
@ -67,6 +75,7 @@ hexo.config.minify.js = Object.assign(jsDefault, hexo.config.minify.js)
|
||||||
hexo.config.minify.svg = Object.assign(svgDefault, hexo.config.minify.svg)
|
hexo.config.minify.svg = Object.assign(svgDefault, hexo.config.minify.svg)
|
||||||
hexo.config.minify.gzip = Object.assign(gzipDefault, hexo.config.minify.gzip)
|
hexo.config.minify.gzip = Object.assign(gzipDefault, hexo.config.minify.gzip)
|
||||||
hexo.config.minify.brotli = Object.assign(brotliDefault, hexo.config.minify.brotli)
|
hexo.config.minify.brotli = Object.assign(brotliDefault, hexo.config.minify.brotli)
|
||||||
|
hexo.config.minify.xml = Object.assign(xmlDefault, hexo.config.minify.xml)
|
||||||
|
|
||||||
if (hexo.config.minify.enable === true) {
|
if (hexo.config.minify.enable === true) {
|
||||||
const filter = require('./lib/filter')
|
const filter = require('./lib/filter')
|
||||||
|
@ -76,6 +85,7 @@ if (hexo.config.minify.enable === true) {
|
||||||
hexo.extend.filter.register('after_generate', filter.minifySvg, hexo.config.minify.svg.priority)
|
hexo.extend.filter.register('after_generate', filter.minifySvg, hexo.config.minify.svg.priority)
|
||||||
hexo.extend.filter.register('after_generate', filter.gzipFn, hexo.config.minify.gzip.priority)
|
hexo.extend.filter.register('after_generate', filter.gzipFn, hexo.config.minify.gzip.priority)
|
||||||
hexo.extend.filter.register('after_generate', filter.brotliFn, hexo.config.minify.brotli.priority)
|
hexo.extend.filter.register('after_generate', filter.brotliFn, hexo.config.minify.brotli.priority)
|
||||||
|
hexo.extend.filter.register('after_generate', filter.minifyXml, hexo.config.minify.xml.priority)
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -85,5 +95,6 @@ module.exports = {
|
||||||
jsDefault,
|
jsDefault,
|
||||||
svgDefault,
|
svgDefault,
|
||||||
gzipDefault,
|
gzipDefault,
|
||||||
brotliDefault
|
brotliDefault,
|
||||||
|
xmlDefault
|
||||||
}
|
}
|
||||||
|
|
|
@ -215,11 +215,50 @@ function brotliFn () {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function minifyXml () {
|
||||||
|
const hexo = this
|
||||||
|
const options = hexo.config.minify.xml
|
||||||
|
if (options.enable === false) return
|
||||||
|
|
||||||
|
const { route } = hexo
|
||||||
|
const routeList = route.list()
|
||||||
|
const { globOptions, include, removeComments, verbose } = options
|
||||||
|
|
||||||
|
return Promise.all((match(routeList, include, globOptions)).map((path) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const assetPath = route.get(path)
|
||||||
|
let assetTxt = ''
|
||||||
|
assetPath.on('data', (chunk) => (assetTxt += chunk))
|
||||||
|
assetPath.on('end', () => {
|
||||||
|
if (assetTxt.length) {
|
||||||
|
try {
|
||||||
|
/* !
|
||||||
|
* Regex patterns are adapted from pretty-data 0.50.0
|
||||||
|
* Licensed MIT (c) 2012-2017 Vadim Kiryukhin ( vkiryukhin @ gmail.com )
|
||||||
|
* https://github.com/vkiryukhin/pretty-data
|
||||||
|
*/
|
||||||
|
const text = removeComments
|
||||||
|
? assetTxt.replace(/<![ \r\n\t]*(--([^-]|[\r\n]|-[^-])*--[ \r\n\t]*)>/g, '')
|
||||||
|
: assetTxt
|
||||||
|
const result = text.replace(/>\s{0,}</g, '><')
|
||||||
|
if (verbose) logFn.call(this, assetTxt, result, path, 'xml')
|
||||||
|
resolve(route.set(path, result))
|
||||||
|
} catch (err) {
|
||||||
|
reject(new Error(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
minifyHtml,
|
minifyHtml,
|
||||||
minifyCss,
|
minifyCss,
|
||||||
minifyJs,
|
minifyJs,
|
||||||
minifySvg,
|
minifySvg,
|
||||||
gzipFn,
|
gzipFn,
|
||||||
brotliFn
|
brotliFn,
|
||||||
|
minifyXml
|
||||||
}
|
}
|
||||||
|
|
|
@ -793,7 +793,6 @@ describe('brotli', () => {
|
||||||
expect(routeList).not.toContain(path.concat('.br'))
|
expect(routeList).not.toContain(path.concat('.br'))
|
||||||
|
|
||||||
const result = await b()
|
const result = await b()
|
||||||
// empty file resolves to an array of undefined
|
|
||||||
expect(result).toBeDefined()
|
expect(result).toBeDefined()
|
||||||
expect(result[0]).toBeUndefined()
|
expect(result[0]).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
@ -966,3 +965,190 @@ describe('brotli', () => {
|
||||||
expect(routeList).toEqual(expect.arrayContaining(expected))
|
expect(routeList).toEqual(expect.arrayContaining(expected))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('xml', () => {
|
||||||
|
const { xmlDefault } = require('../index')
|
||||||
|
const x = require('../lib/filter').minifyXml.bind(hexo)
|
||||||
|
const path = 'foo.xml'
|
||||||
|
const input = '<?xml version="1.0" encoding="utf-8"?>\n<feed xmlns="http://www.w3.org/2005/Atom">\n <!-- foo bar -->\n <title>foo</title>\n</feed>'
|
||||||
|
const expected = '<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title>foo</title></feed>'
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
hexo.config.minify.xml = Object.assign({}, xmlDefault)
|
||||||
|
// plugin is disabled by default
|
||||||
|
hexo.config.minify.xml.enable = true
|
||||||
|
hexo.route.set(path, input)
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
const routeList = hexo.route.list()
|
||||||
|
routeList.forEach((path) => hexo.route.remove(path))
|
||||||
|
})
|
||||||
|
|
||||||
|
test('default', async () => {
|
||||||
|
await x()
|
||||||
|
|
||||||
|
const output = hexo.route.get(path)
|
||||||
|
let result = ''
|
||||||
|
output.on('data', (chunk) => (result += chunk))
|
||||||
|
output.on('end', () => {
|
||||||
|
expect(result).toBe(expected)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('disable', async () => {
|
||||||
|
hexo.config.minify.xml.enable = false
|
||||||
|
const result = await x()
|
||||||
|
|
||||||
|
expect(result).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('option - removeComments', async () => {
|
||||||
|
hexo.config.minify.xml.removeComments = false
|
||||||
|
|
||||||
|
await x()
|
||||||
|
|
||||||
|
const output = hexo.route.get(path)
|
||||||
|
let result = ''
|
||||||
|
output.on('data', (chunk) => (result += chunk))
|
||||||
|
output.on('end', () => {
|
||||||
|
expect(result).toContain('<!-- foo bar -->')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('option - verbose', async () => {
|
||||||
|
hexo.config.minify.xml.verbose = true
|
||||||
|
hexo.log.log = jest.fn()
|
||||||
|
await x()
|
||||||
|
|
||||||
|
expect(hexo.log.log.mock.calls[0][0]).toContain(`xml: ${path}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('empty file', async () => {
|
||||||
|
hexo.route.set(path, '')
|
||||||
|
const result = await x()
|
||||||
|
|
||||||
|
expect(result).toBeDefined()
|
||||||
|
expect(result[0]).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('include - exclude *.min.xml by default', async () => {
|
||||||
|
const path = 'foo.min.xml'
|
||||||
|
hexo.route.set(path, input)
|
||||||
|
await x()
|
||||||
|
|
||||||
|
const output = hexo.route.get(path)
|
||||||
|
let result = ''
|
||||||
|
output.on('data', (chunk) => (result += chunk))
|
||||||
|
output.on('end', () => {
|
||||||
|
expect(result).toBe(input)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('include - basename', async () => {
|
||||||
|
hexo.config.minify.xml.include = 'bar.xml'
|
||||||
|
const path = 'foo/bar.xml'
|
||||||
|
hexo.route.set(path, input)
|
||||||
|
await x()
|
||||||
|
|
||||||
|
const output = hexo.route.get(path)
|
||||||
|
let result = ''
|
||||||
|
output.on('data', (chunk) => (result += chunk))
|
||||||
|
output.on('end', () => {
|
||||||
|
expect(result).toBe(expected)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('include - slash in pattern', async () => {
|
||||||
|
hexo.config.minify.xml.include = '**/lectus/**/*.xml'
|
||||||
|
const path = 'eleifend/lectus/nullam/dapibus/netus.xml'
|
||||||
|
hexo.route.set(path, input)
|
||||||
|
await x()
|
||||||
|
|
||||||
|
const output = hexo.route.get(path)
|
||||||
|
let result = ''
|
||||||
|
output.on('data', (chunk) => (result += chunk))
|
||||||
|
output.on('end', () => {
|
||||||
|
expect(result).toBe(expected)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('include - basename + slash', async () => {
|
||||||
|
hexo.route.remove(path)
|
||||||
|
|
||||||
|
const paths = [
|
||||||
|
'lorem/ipsum/dolor.xml',
|
||||||
|
'gravida/sociis/erat/ante.xml',
|
||||||
|
'aptent/elementum.xml',
|
||||||
|
'felis/blandit/cursus.xml'
|
||||||
|
]
|
||||||
|
hexo.config.minify.xml.include = [
|
||||||
|
'dolor.xml',
|
||||||
|
'**/sociis/**/*.xml'
|
||||||
|
]
|
||||||
|
|
||||||
|
paths.forEach((inpath) => {
|
||||||
|
hexo.route.set(inpath, input)
|
||||||
|
})
|
||||||
|
await x()
|
||||||
|
|
||||||
|
const minPaths = paths.slice(0, 2)
|
||||||
|
const unminPaths = paths.slice(2)
|
||||||
|
|
||||||
|
minPaths.forEach((inpath) => {
|
||||||
|
const output = hexo.route.get(inpath)
|
||||||
|
let result = ''
|
||||||
|
output.on('data', (chunk) => (result += chunk))
|
||||||
|
output.on('end', () => {
|
||||||
|
expect(result).toBe(expected)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
unminPaths.forEach((inpath) => {
|
||||||
|
const output = hexo.route.get(inpath)
|
||||||
|
let result = ''
|
||||||
|
output.on('data', (chunk) => (result += chunk))
|
||||||
|
output.on('end', () => {
|
||||||
|
expect(result).toBe(input)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('include - reverse pattern + basename disabled', async () => {
|
||||||
|
hexo.route.remove(path)
|
||||||
|
|
||||||
|
const paths = [
|
||||||
|
'lorem/ipsum/dolor.xml',
|
||||||
|
'gravida/sociis/erat/ante.xml',
|
||||||
|
'aptent/elementum.xml',
|
||||||
|
'felis/blandit/cursus.xml'
|
||||||
|
]
|
||||||
|
hexo.config.minify.xml.include = [
|
||||||
|
'!dolor.xml'
|
||||||
|
]
|
||||||
|
hexo.config.minify.xml.globOptions = {
|
||||||
|
basename: false
|
||||||
|
}
|
||||||
|
|
||||||
|
paths.forEach((inpath) => {
|
||||||
|
hexo.route.set(inpath, input)
|
||||||
|
})
|
||||||
|
await x()
|
||||||
|
|
||||||
|
paths.forEach((inpath) => {
|
||||||
|
const output = hexo.route.get(inpath)
|
||||||
|
let result = ''
|
||||||
|
output.on('data', (chunk) => (result += chunk))
|
||||||
|
output.on('end', () => {
|
||||||
|
expect(result).toBe(expected)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('include - empty route', async () => {
|
||||||
|
hexo.route.remove(path)
|
||||||
|
|
||||||
|
const result = await x()
|
||||||
|
expect(result.length).toBe(0)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
Loading…
Reference in New Issue