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)
|
||||
- [Gzip](#gzip)
|
||||
- [Brotli](#brotli)
|
||||
- [XML](#xml)
|
||||
- [Globbing](#globbing)
|
||||
- [HTTP Compression](#http-compression)
|
||||
|
||||
|
@ -191,6 +192,24 @@ minify:
|
|||
- **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)
|
||||
|
||||
## 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
|
||||
|
||||
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'],
|
||||
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.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.gzip = Object.assign(gzipDefault, hexo.config.minify.gzip)
|
||||
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) {
|
||||
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.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.minifyXml, hexo.config.minify.xml.priority)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
@ -85,5 +95,6 @@ module.exports = {
|
|||
jsDefault,
|
||||
svgDefault,
|
||||
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 = {
|
||||
minifyHtml,
|
||||
minifyCss,
|
||||
minifyJs,
|
||||
minifySvg,
|
||||
gzipFn,
|
||||
brotliFn
|
||||
brotliFn,
|
||||
minifyXml
|
||||
}
|
||||
|
|
|
@ -793,7 +793,6 @@ describe('brotli', () => {
|
|||
expect(routeList).not.toContain(path.concat('.br'))
|
||||
|
||||
const result = await b()
|
||||
// empty file resolves to an array of undefined
|
||||
expect(result).toBeDefined()
|
||||
expect(result[0]).toBeUndefined()
|
||||
})
|
||||
|
@ -966,3 +965,190 @@ describe('brotli', () => {
|
|||
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