mirror of https://github.com/curbengh/hexo-yam
feat: minify json
This commit is contained in:
parent
64ba16cb22
commit
fdcdbef4a1
24
README.md
24
README.md
|
@ -7,7 +7,7 @@
|
||||||
[![Known Vulnerabilities](https://snyk.io/test/npm/hexo-yam/badge.svg)](https://snyk.io/test/npm/hexo-yam)
|
[![Known Vulnerabilities](https://snyk.io/test/npm/hexo-yam/badge.svg)](https://snyk.io/test/npm/hexo-yam)
|
||||||
[![Greenkeeper badge](https://badges.greenkeeper.io/curbengh/hexo-yam.svg)](https://greenkeeper.io/)
|
[![Greenkeeper badge](https://badges.greenkeeper.io/curbengh/hexo-yam.svg)](https://greenkeeper.io/)
|
||||||
|
|
||||||
Yet Another Minifier for Hexo. Minify and compress HTML, JS, CSS and SVG. XML, JSON and [many more](https://github.com/curbengh/hexo-yam/blob/ba77db0094a7c07ea9f70f010bfc15541d4105ca/index.js#L64) are also compressed. Support gzip and [brotli](https://en.wikipedia.org/wiki/Brotli) [compressions](https://en.wikipedia.org/wiki/HTTP_compression).
|
Yet Another Minifier for Hexo. Minify and compress HTML, JS, CSS, SVG, XML and JSON. [Other files](https://github.com/curbengh/hexo-yam/blob/ba77db0094a7c07ea9f70f010bfc15541d4105ca/index.js#L64) are also compressed. Support gzip and [brotli](https://en.wikipedia.org/wiki/Brotli) [compressions](https://en.wikipedia.org/wiki/HTTP_compression).
|
||||||
|
|
||||||
|
|
||||||
## Table of contents
|
## Table of contents
|
||||||
|
@ -22,6 +22,7 @@ Yet Another Minifier for Hexo. Minify and compress HTML, JS, CSS and SVG. XML, J
|
||||||
- [Gzip](#gzip)
|
- [Gzip](#gzip)
|
||||||
- [Brotli](#brotli)
|
- [Brotli](#brotli)
|
||||||
- [XML](#xml)
|
- [XML](#xml)
|
||||||
|
- [JSON](#json)
|
||||||
- [Globbing](#globbing)
|
- [Globbing](#globbing)
|
||||||
- [HTTP Compression](#http-compression)
|
- [HTTP Compression](#http-compression)
|
||||||
|
|
||||||
|
@ -194,6 +195,8 @@ minify:
|
||||||
|
|
||||||
## XML
|
## XML
|
||||||
|
|
||||||
|
Remove whitespaces in xml.
|
||||||
|
|
||||||
``` yaml
|
``` yaml
|
||||||
minify:
|
minify:
|
||||||
xml:
|
xml:
|
||||||
|
@ -210,6 +213,25 @@ minify:
|
||||||
- **removeComments** - Remove [comments](https://developer.mozilla.org/en-US/docs/Web/XML/XML_introduction) in xml. Defaults to `true`.
|
- **removeComments** - Remove [comments](https://developer.mozilla.org/en-US/docs/Web/XML/XML_introduction) in xml. Defaults to `true`.
|
||||||
- **globOptions** - See [globbing](#globbing) section.
|
- **globOptions** - See [globbing](#globbing) section.
|
||||||
|
|
||||||
|
## JSON
|
||||||
|
|
||||||
|
Remove whitespaces in json.
|
||||||
|
|
||||||
|
``` yaml
|
||||||
|
minify:
|
||||||
|
json:
|
||||||
|
enable: false
|
||||||
|
include:
|
||||||
|
- '*.json'
|
||||||
|
- '!*.min.json'
|
||||||
|
```
|
||||||
|
- **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.json` by default.
|
||||||
|
- **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.
|
||||||
|
|
12
index.js
12
index.js
|
@ -67,6 +67,13 @@ const xmlDefault = {
|
||||||
removeComments: true,
|
removeComments: true,
|
||||||
globOptions: { basename: true }
|
globOptions: { basename: true }
|
||||||
}
|
}
|
||||||
|
const jsonDefault = {
|
||||||
|
enable: false,
|
||||||
|
priority: 10,
|
||||||
|
verbose: false,
|
||||||
|
include: ['*.json', '!*.min.json'],
|
||||||
|
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)
|
||||||
|
@ -76,6 +83,7 @@ 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)
|
hexo.config.minify.xml = Object.assign(xmlDefault, hexo.config.minify.xml)
|
||||||
|
hexo.config.minify.json = Object.assign(jsonDefault, hexo.config.minify.json)
|
||||||
|
|
||||||
if (hexo.config.minify.enable === true) {
|
if (hexo.config.minify.enable === true) {
|
||||||
const filter = require('./lib/filter')
|
const filter = require('./lib/filter')
|
||||||
|
@ -86,6 +94,7 @@ if (hexo.config.minify.enable === true) {
|
||||||
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)
|
hexo.extend.filter.register('after_generate', filter.minifyXml, hexo.config.minify.xml.priority)
|
||||||
|
hexo.extend.filter.register('after_generate', filter.minifyJson, hexo.config.minify.json.priority)
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -96,5 +105,6 @@ module.exports = {
|
||||||
svgDefault,
|
svgDefault,
|
||||||
gzipDefault,
|
gzipDefault,
|
||||||
brotliDefault,
|
brotliDefault,
|
||||||
xmlDefault
|
xmlDefault,
|
||||||
|
jsonDefault
|
||||||
}
|
}
|
||||||
|
|
|
@ -253,6 +253,36 @@ function minifyXml () {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function minifyJson () {
|
||||||
|
const hexo = this
|
||||||
|
const options = hexo.config.minify.json
|
||||||
|
if (options.enable === false) return
|
||||||
|
|
||||||
|
const { route } = hexo
|
||||||
|
const routeList = route.list()
|
||||||
|
const { globOptions, include, 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 {
|
||||||
|
const result = JSON.stringify(JSON.parse(assetTxt))
|
||||||
|
if (verbose) logFn.call(this, assetTxt, result, path, 'json')
|
||||||
|
resolve(route.set(path, result))
|
||||||
|
} catch (err) {
|
||||||
|
reject(new Error(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
minifyHtml,
|
minifyHtml,
|
||||||
minifyCss,
|
minifyCss,
|
||||||
|
@ -260,5 +290,6 @@ module.exports = {
|
||||||
minifySvg,
|
minifySvg,
|
||||||
gzipFn,
|
gzipFn,
|
||||||
brotliFn,
|
brotliFn,
|
||||||
minifyXml
|
minifyXml,
|
||||||
|
minifyJson
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,188 @@
|
||||||
|
/* eslint-env jest */
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const Hexo = require('hexo')
|
||||||
|
const hexo = new Hexo(__dirname)
|
||||||
|
global.hexo = hexo
|
||||||
|
const { jsonDefault } = require('../index')
|
||||||
|
const jsonFn = require('../lib/filter').minifyJson.bind(hexo)
|
||||||
|
const path = 'foo.json'
|
||||||
|
const input = '{\n\t"vitae": "hendrerit",\n\t"tristique": [\n\t\t"primis",\n\t\t"quam"\n\t]\n}'
|
||||||
|
const expected = '{"vitae":"hendrerit","tristique":["primis","quam"]}'
|
||||||
|
|
||||||
|
describe('xml', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
hexo.config.minify.json = Object.assign({}, jsonDefault)
|
||||||
|
// plugin is disabled by default
|
||||||
|
hexo.config.minify.json.enable = true
|
||||||
|
hexo.route.set(path, input)
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
const routeList = hexo.route.list()
|
||||||
|
routeList.forEach((path) => hexo.route.remove(path))
|
||||||
|
})
|
||||||
|
|
||||||
|
test('default', async () => {
|
||||||
|
await jsonFn()
|
||||||
|
|
||||||
|
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.json.enable = false
|
||||||
|
const result = await jsonFn()
|
||||||
|
|
||||||
|
expect(result).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('option - verbose', async () => {
|
||||||
|
hexo.config.minify.json.verbose = true
|
||||||
|
hexo.log.log = jest.fn()
|
||||||
|
await jsonFn()
|
||||||
|
|
||||||
|
expect(hexo.log.log.mock.calls[0][0]).toContain(`json: ${path}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('invalid input', async () => {
|
||||||
|
hexo.route.set(path, 'foo')
|
||||||
|
try {
|
||||||
|
await jsonFn()
|
||||||
|
} catch (err) {
|
||||||
|
expect(err.message).toContain('SyntaxError')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('empty file', async () => {
|
||||||
|
hexo.route.set(path, '')
|
||||||
|
const result = await jsonFn()
|
||||||
|
|
||||||
|
expect(result).toBeDefined()
|
||||||
|
expect(result[0]).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('include - exclude *.min.json by default', async () => {
|
||||||
|
const path = 'foo.min.json'
|
||||||
|
hexo.route.set(path, input)
|
||||||
|
await jsonFn()
|
||||||
|
|
||||||
|
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.json.include = 'bar.json'
|
||||||
|
const path = 'foo/bar.json'
|
||||||
|
hexo.route.set(path, input)
|
||||||
|
await jsonFn()
|
||||||
|
|
||||||
|
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.json.include = '**/lectus/**/*.json'
|
||||||
|
const path = 'eleifend/lectus/nullam/dapibus/netus.json'
|
||||||
|
hexo.route.set(path, input)
|
||||||
|
await jsonFn()
|
||||||
|
|
||||||
|
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.json',
|
||||||
|
'gravida/sociis/erat/ante.json',
|
||||||
|
'aptent/elementum.json',
|
||||||
|
'felis/blandit/cursus.json'
|
||||||
|
]
|
||||||
|
hexo.config.minify.json.include = [
|
||||||
|
'dolor.json',
|
||||||
|
'**/sociis/**/*.json'
|
||||||
|
]
|
||||||
|
|
||||||
|
paths.forEach((inpath) => {
|
||||||
|
hexo.route.set(inpath, input)
|
||||||
|
})
|
||||||
|
await jsonFn()
|
||||||
|
|
||||||
|
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.json',
|
||||||
|
'gravida/sociis/erat/ante.json',
|
||||||
|
'aptent/elementum.json',
|
||||||
|
'felis/blandit/cursus.json'
|
||||||
|
]
|
||||||
|
hexo.config.minify.json.include = [
|
||||||
|
'!dolor.json'
|
||||||
|
]
|
||||||
|
hexo.config.minify.json.globOptions = {
|
||||||
|
basename: false
|
||||||
|
}
|
||||||
|
|
||||||
|
paths.forEach((inpath) => {
|
||||||
|
hexo.route.set(inpath, input)
|
||||||
|
})
|
||||||
|
await jsonFn()
|
||||||
|
|
||||||
|
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 jsonFn()
|
||||||
|
expect(result.length).toBe(0)
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in New Issue