Compare commits

..

No commits in common. "master" and "v1.3.1" have entirely different histories.

25 changed files with 303 additions and 3169 deletions

View File

@ -1,10 +0,0 @@
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"

View File

@ -1,22 +0,0 @@
name: Linter
on: [push, pull_request]
jobs:
linter:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Install Bun
uses: oven-sh/setup-bun@v2
- name: Cache NPM dependencies
uses: actions/cache@v4
with:
path: node_modules
key: ${{ runner.os }}-npm-cache
restore-keys: |
${{ runner.os }}-npm-cache
- name: Install Dependencies
run: bun install
- name: Lint
run: bun run lint

View File

@ -1,23 +0,0 @@
name: Semgrep
on:
pull_request: {}
push:
branches:
- master
paths:
- .github/workflows/semgrep.yml
schedule:
# Weekly
- cron: "0 0 * * 0"
jobs:
semgrep:
name: Scan
runs-on: ubuntu-latest
env:
SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
container:
image: returntocorp/semgrep
if: (github.actor != 'dependabot[bot]')
steps:
- uses: actions/checkout@v5
- run: semgrep ci

View File

@ -1,37 +0,0 @@
name: Snyk
on:
schedule:
# Weekly
- cron: "0 0 * * 0"
pull_request:
branches:
- "dependabot/github_actions/github/codeql-action**"
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Install Bun
uses: oven-sh/setup-bun@v2
- name: Install Dependencies
run: bun install
- name: Run Snyk to check for vulnerabilities
uses: snyk/actions/node@master
continue-on-error: true # To make sure that SARIF upload gets called
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
command: test
args: --sarif-file-output=snyk.sarif
- name: Upload result to GitHub Code Scanning
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: snyk.sarif
- name: Monitor for vulnerabilities
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
command: monitor

View File

@ -1,52 +0,0 @@
name: Tester
on: [push, pull_request]
jobs:
tester:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: ["20", "22", "24"]
fail-fast: false
steps:
- uses: actions/checkout@v5
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Cache NPM dependencies
uses: actions/cache@v4
with:
path: node_modules
key: ${{ runner.os }}-npm-cache
restore-keys: ${{ runner.os }}-npm-cache
- name: Determine unrs-resolver binary version
shell: bash
run: |
case "$RUNNER_OS" in
"Linux")
echo "PLATFORM=linux-x64-gnu" >> "$GITHUB_ENV" ;;
"Windows")
echo "PLATFORM=win32-x64-msvc" >> "$GITHUB_ENV" ;;
"macOS")
echo "PLATFORM=darwin-arm64" >> "$GITHUB_ENV" ;;
esac
- name: Install Dependencies
shell: bash
run: |
npm install
npm install --include=optional --force @mongodb-js/zstd
npm install "@unrs/resolver-binding-$PLATFORM"
- name: Test
run: npm run test
env:
CI: true
- name: Upload coverage report to Codecov
if: matrix.os == 'ubuntu-latest' && matrix.node-version == '22'
uses: codecov/codecov-action@v5
with:
fail_ci_if_error: true
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

2
.gitignore vendored
View File

@ -3,5 +3,3 @@ node_modules/
package-lock.json package-lock.json
tmp/ tmp/
*.log *.log
coverage/
bun.lockb

7
.npmignore Normal file
View File

@ -0,0 +1,7 @@
.DS_Store
node_modules/
tmp/
*.log
package-lock.json
.travis.yml
.snyk

13
.snyk
View File

@ -2,4 +2,15 @@
version: v1.12.0 version: v1.12.0
# ignores vulnerabilities until expiry date; change duration by modifying expiry date # ignores vulnerabilities until expiry date; change duration by modifying expiry date
ignore: ignore:
patch: 'npm:braces:20180219':
- hexo-fs > chokidar > anymatch > micromatch > braces:
reason: Patch/update unavailable
expires: '2018-12-31T00:00:00.000Z'
'npm:chownr:20180731':
- hexo-fs > chokidar > fsevents > node-pre-gyp > tar > chownr:
reason: Patch/update unavailable
expires: '2018-12-31T00:00:00.000Z'
- iltorb > prebuild-install > tar-fs > chownr:
reason: Patch/update unavailable
expires: '2018-12-31T00:00:00.000Z'
patch: {}

20
.travis.yml Normal file
View File

@ -0,0 +1,20 @@
language: node_js
node_js:
- "node" # latest stable Node.js release
cache:
directories:
- "node_modules" # cache the modules for faster build
script:
- npm install -g snyk
- npm install # install node modules
- snyk auth $SNYK_TOKEN
- snyk test # Check node modules for vulnerability
- snyk protect # Patch node modules (if available)
- snyk monitor # Update dependencies to snyk
branches:
only:
- master # Only build master branch
- /^greenkeeper.*$/ # Greenkeeper branches

21
LICENSE
View File

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2018-2024 curbengh, 2016-2018 rozbo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

335
README.md
View File

@ -1,307 +1,108 @@
# hexo-yam # Hexo-yam
[![npm version](https://img.shields.io/npm/v/hexo-yam?logo=npm)](https://www.npmjs.com/package/hexo-yam) [![npm version](https://badge.fury.io/js/hexo-yam.svg)](https://www.npmjs.com/package/hexo-yam)
[![Build Status](https://img.shields.io/github/actions/workflow/status/curbengh/hexo-yam/tester.yml?branch=master&logo=github)](https://github.com/curbengh/hexo-yam/actions?query=workflow%3ATester) [![Build Status](https://travis-ci.com/weyusi/hexo-yam.svg?branch=master)](https://travis-ci.com/weyusi/hexo-yam)
[![codecov](https://img.shields.io/codecov/c/gh/curbengh/hexo-yam?logo=codecov)](https://codecov.io/gh/curbengh/hexo-yam) [![NPM Dependencies](https://david-dm.org/weyusi/hexo-yam.svg)](https://david-dm.org/weyusi/hexo-yam)
[![NPM Dependencies](https://img.shields.io/librariesio/release/npm/hexo-yam)](https://libraries.io/npm/hexo-yam) [![Known Vulnerabilities](https://snyk.io/test/npm/hexo-yam/badge.svg)](https://snyk.io/test/npm/hexo-yam)
[![Known Vulnerabilities](https://snyk.io/test/github/curbengh/hexo-yam/badge.svg)](https://snyk.io/test/github/curbengh/hexo-yam) [![Greenkeeper badge](https://badges.greenkeeper.io/weyusi/hexo-yam.svg)](https://greenkeeper.io/)
Yet Another Minifier for Hexo. Minify HTML, JS, CSS, SVG, XML and JSON. Compress static [web assets](https://github.com/curbengh/hexo-yam/blob/ba77db0094a7c07ea9f70f010bfc15541d4105ca/index.js#L64) using gzip, brotli and zstd. > This project is based on [hexo-neat](https://github.com/rozbo/hexo-neat)
## Table of contents Yet Another Minifier for Hexo. Minify and compress html, js and css. xml, json and [many more](https://github.com/weyusi/hexo-yam/blob/master/lib/filter.js#L105) are also compressed. Support gzip and [brotli](https://en.wikipedia.org/wiki/Brotli) [compressions](https://en.wikipedia.org/wiki/HTTP_compression).
- [Installation](#installation) The original package has not been [updated](https://www.npmjs.com/package/hexo-neat) for a while. I update the [dependencies](https://github.com/weyusi/hexo-yam/blob/master/package.json) and add compression support.
- [Options](#options)
- [HTML](#html) All the options are the same, so you can use this as a drop-in replacement.
- [CSS](#css)
- [JS](#js) *Note:* See [HTTP Compression](#http-compression) section below for more info on using brotli.
- [SVG](#svg)
- [XML](#xml)
- [JSON](#json)
- [Gzip](#gzip)
- [Brotli](#brotli)
- [Zstd](#zstd)
- [Globbing](#globbing)
## Installation ## Installation
``` bash
```bash $ npm install hexo-yam --save
$ npm install --save hexo-yam
``` ```
## Usage
To enable this plugin, insert the following to `_config.yml`:
``` yaml
neat_enable: true
```
For further customization, see below.
## Options ## Options
``` yaml
```yaml neat_html:
minify:
enable: true enable: true
previewServer: true exclude:
html:
css:
js:
svg:
gzip:
brotli:
xml:
json:
``` ```
- **enable** - Enable the plugin. Defaults to `true`. - **enable** - Enable the plugin. Defaults to `true`.
- **previewServer** - Disable the plugin when running `hexo server`. Defaults to `true`. - **logger** - Verbose output. Defaults to `false`.
- **html** - See [HTML](#html) section - **exclude** - Exclude files
- **css** - See [CSS](#css) section
- **js** - See [JS](#js) section
- **svg** - See [SVG](#svg) section
- **gzip** - See [Gzip](#gzip) section
- **brotli** - See [Brotli](#brotli) section
- **xml** - See [XML](#xml) section
- **json** - See [JSON](#json) section
## HTML
```yaml
minify:
html:
enable: true
exclude:
```
- **enable** - Enable the plugin. Defaults to `true`.
- **priority** - Plugin's priority. Defaults to `10`. Set lower value to set higher priority and vice versa.
- **verbose** - Verbose output. Defaults to `false`.
- **exclude** - Exclude files. Support [wildcard](http://www.globtester.com/) pattern(s) in a string or array.
- **globOptions** - See [globbing](#globbing) section.
For more options, see [HTMLMinifier](https://github.com/kangax/html-minifier). For more options, see [HTMLMinifier](https://github.com/kangax/html-minifier).
## CSS ----------
```yaml ``` yaml
minify: neat_css:
css: enable: true
enable: true exclude:
exclude: - '*.min.css'
- "*.min.css"
``` ```
- **enable** - Enable the plugin. Defaults to `true`. - **enable** - Enable the plugin. Defaults to `true`.
- **priority** - Plugin's priority. Defaults to `10`. - **logger** - Verbose output. Defaults to `false`.
- **verbose** - Verbose output. Defaults to `false`. - **exclude** - Exclude files
- **exclude** - Exclude files. Support [wildcard](http://www.globtester.com/) pattern(s) in a string or array.
- **level** - Optimization level. Defaults to `2`.
- **globOptions** - See [globbing](#globbing) section.
For more options, see [clean-css](https://github.com/jakubpawlowicz/clean-css). ----------
## JS ``` yaml
neat_js:
```yaml enable: true
minify: exclude:
js: - '*.min.js'
enable: true
exclude:
- "*.min.js"
``` ```
- **enable** - Enable the plugin. Defaults to `true`. - **enable** - Enable the plugin. Defaults to `true`.
- **priority** - Plugin's priority. Defaults to `10`. - **mangle** - Mangle file names. Defaults to `true`.
- **verbose** - Verbose output. Defaults to `false`. - **logger** - Verbose output. Defaults to `false`.
- **exclude** - Exclude files. Support [wildcard](http://www.globtester.com/) pattern(s) in a string or array. - **output** - Output options
- **compress** - Compress options. - **compress** - Compress options
- **mangle** - Mangle variable names. Defaults to `true`. Pass an object to specify [mangle options](https://github.com/terser-js/terser#mangle-options). - **exclude** - Exclude files
- **output** - Output options.
- To retain comments, `output: {comments: true}`.
- **globOptions** - See [globbing](#globbing) section.
For more options, see [Terser](https://github.com/terser-js/terser). For more options, see [UglifyJS](https://github.com/mishoo/UglifyJS2).
## SVG ----------
```yaml ``` yaml
minify: neat_gzip:
svg: enable: true
enable: true
include:
- "*.svg"
- "!*.min.svg"
``` ```
- **enable** - Enable the plugin. Defaults to `true`. - **enable** - Enable the plugin. Defaults to `true`.
- **priority** - Plugin's priority. Defaults to `10`. - **logger** - Verbose output. Defaults to `false`.
- **verbose** - Verbose output. Defaults to `false`.
- **include** - Include files. Support [wildcard](http://www.globtester.com/) pattern(s) in a string or array.
- Exclude `*.min.svg` by default.
- **plugins** - Plugin options.
- Examples:
```yaml
plugins:
# Retain comments
removeComments: false
# Do not remove unused ID attributes
cleanupIds: false
```
- For more options, see [svgo](https://github.com/svg/svgo).
- **globOptions** - See [globbing](#globbing) section.
## XML ----------
Remove whitespaces in xml. ``` yaml
neat_brotli:
```yaml enable: true
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.
For more options, see [minify-xml](https://github.com/kristian/minify-xml#options).
## 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.
## Gzip
```yaml
minify:
gzip:
enable: true
include:
- "*.html"
- "*.css"
- "*.js"
- "*.txt"
- "*.ttf"
- "*.atom"
- "*.stl"
- "*.xml"
- "*.svg"
- "*.eot"
- "*.json"
```
- **enable** - Enable the plugin. Defaults to `true`. - **enable** - Enable the plugin. Defaults to `true`.
- **priority** - Plugin's priority. Defaults to `10`. - **logger** - Verbose output. Defaults to `false`.
- **verbose** - Verbose output. Defaults to `false`.
- **include** - Include files. Support [wildcard](http://www.globtester.com/) pattern(s) in a string or array.
- Support one-liner, `include: ['*.html','*.css','*.js']`.
- Must include asterisk and single quotes. `.html` is invalid. `'*.html'` is valid.
- **globOptions** - See [globbing](#globbing) section.
- **level** - Compression level; lower value may results in faster compression but slightly larger (compressed) file. Range `1-9`. Defaults to `9`, or the value of [`zlib.constants.Z_BEST_COMPRESSION`](https://nodejs.org/docs/latest-v12.x/api/zlib.html#zlib_zlib_constants)
## Brotli ## HTTP Compression
While most modern web browsers [support](https://www.caniuse.com/#feat=brotli) Brotli, you also need to consider whether the web/app server, hosting platform, reverse proxy or CDN (whichever relevant to you) support it.
```yaml As of 2018, GitHub/GitLab Pages and Netlify *do not* support brotli. You can generate `.br` files, but they won't be serving those files.
minify:
brotli:
enable: true
include:
- "*.html"
- "*.css"
- "*.js"
- "*.txt"
- "*.ttf"
- "*.atom"
- "*.stl"
- "*.xml"
- "*.svg"
- "*.eot"
- "*.json"
```
- **enable** - Enable the plugin. Defaults to `true`. If you have access to the web server config, you should disable on-the-fly compression for static files (that are already compressed by this plugin), e.g.
- **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.
- **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)
## Zstd - [nginx](https://github.com/google/ngx_brotli): Make sure both filter and static modules are enabled. This way pre-compressed `.br` files will be served while dynamic content can be compressed on-the-fly. Protip: `brotli_types text/plain text/css application/javascript application/json image/svg+xml application/xml+rss;` to prevent compressing media files (which are already compressed anyway).
- [Apache](https://httpd.apache.org/docs/2.4/en/mod/mod_brotli.html): See 'Serving pre-compressed content' section of [mod_brotli](https://httpd.apache.org/docs/2.4/en/mod/mod_brotli.html).
```yaml - [Caddy](https://caddyserver.com/features): [0.9.4+](https://caddyserver.com/blog/caddy-0_9_4-released) by default support pre-compressed `.gz` `.br` files and on-the-fly gzip compress dynamic files.
minify: - [express](https://github.com/expressjs/express)/[connect](https://github.com/senchalabs/connect): Use [pre-compressed-assets](https://github.com/domadams/pre-compressed-assets). You still can continue to use [compression](https://github.com/expressjs/compression)/[shrink-ray-current](https://github.com/Alorel/shrink-ray) for dynamic files.
zstd:
enable: false
include:
- "*.html"
- "*.css"
- "*.js"
- "*.txt"
- "*.ttf"
- "*.atom"
- "*.stl"
- "*.xml"
- "*.svg"
- "*.eot"
- "*.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.
- **globOptions** - See [globbing](#globbing) section.
- **level** - Compression level. Range `1-22`. Defaults to `3`, or the value of [`DEFAULT_LEVEL`](https://github.com/mongodb-js/zstd/blob/a3a08c61c9045411c8275e248498dbc583457fb5/src/lib.rs#L9)
## Globbing
Use "globOptions" to customise how glob patterns match files. Refer to [micromatch](https://github.com/micromatch/micromatch#options) for available options.
- basename is enabled by default, unless the pattern has a slash.
- basename is disabled depending on each pattern.
- This means the following options would work,
```yml
exclude:
- "*foo.html" # basename is enabled
- "**/bar/*/*.html" # basename is automatically disabled
- "*baz.css" # basename is enabled
globOptions:
basename: true # default
```
- This behaviour doesn't apply to pattern that starts with `!` (negation).
- This limitation only applies to `include:` option used in svg, gzip and brotli.
- This means the following options would _not_ work,
```yml
include:
- "!foo.svg"
- "!**/bar/*/*.svg"
globOptions:
basename: true
```
- basename will stay disabled, if explicitly disabled in "globOptions".
## Credits ## Credits
All credits go to the following work: All credits go to the following work:
- [hexo-neat](https://github.com/rozbo/hexo-neat) by rozbo - [hexo-neat](https://github.com/rozbo/hexo-neat) by rozbo
- neat html by [HTMLMinifier](https://github.com/kangax/html-minifier)
- neat css by [clean-css](https://github.com/jakubpawlowicz/clean-css)
- neat js by [terser](https://github.com/terser-js/terser)
- gzip feature is inspired by [hexo-generator-optimize](https://github.com/JackyRen/hexo-generator-optimize) - gzip feature is inspired by [hexo-generator-optimize](https://github.com/JackyRen/hexo-generator-optimize)

View File

@ -1,5 +0,0 @@
[install]
optional = false
[install.lockfile]
save = false

145
index.js
View File

@ -1,108 +1,53 @@
/* global hexo */ /* global hexo */
'use strict' if (hexo.config.neat_enable === true) {
// HTML minifier
hexo.config.neat_html = Object.assign({
enable: true,
logger: false,
exclude: [],
ignoreCustomComments: [/^\s*more/],
removeComments: true,
removeCommentsFromCDATA: true,
collapseWhitespace: true,
collapseBooleanAttributes: true,
removeEmptyAttributes: true,
minifyJS: true,
minifyCSS: true
}, hexo.config.neat_html)
hexo.config.minify = Object.assign({ // Css minifier
enable: true, hexo.config.neat_css = Object.assign({
previewServer: true enable: true,
}, hexo.config.minify) logger: false,
exclude: ['*.min.css']
}, hexo.config.neat_css)
hexo.config.minify.html = Object.assign({ // Js minifier
enable: true, hexo.config.neat_js = Object.assign({
priority: 10, enable: true,
verbose: false, mangle: true,
exclude: [], logger: false,
collapseBooleanAttributes: true, output: {},
collapseWhitespace: true, compress: {},
// Ignore '<!-- more -->' https://hexo.io/docs/tag-plugins#Post-Excerpt exclude: ['*.min.js']
ignoreCustomComments: [/^\s*more/], }, hexo.config.neat_js)
removeComments: true,
removeEmptyAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
minifyJS: true,
minifyCSS: true,
globOptions: { basename: true }
}, hexo.config.minify.html)
hexo.config.minify.css = Object.assign({ // html, css, js compression
enable: true, hexo.config.neat_gzip = Object.assign({
priority: 10, enable: true,
verbose: false, logger: false
exclude: ['*.min.css'], }, hexo.config.neat_gzip)
level: 2,
globOptions: { basename: true }
}, hexo.config.minify.css)
hexo.config.minify.js = Object.assign({ // html, css, js compression
enable: true, hexo.config.neat_brotli = Object.assign({
priority: 10, enable: true,
verbose: false, logger: false
exclude: ['*.min.js'], }, hexo.config.neat_brotli)
compress: {},
mangle: true,
output: {},
globOptions: { basename: true }
}, hexo.config.minify.js)
hexo.config.minify.svg = Object.assign({
enable: true,
priority: 10,
verbose: false,
include: ['*.svg', '!*.min.svg'],
plugins: {},
globOptions: { basename: true }
}, hexo.config.minify.svg)
hexo.config.minify.gzip = Object.assign({
enable: true,
priority: 10,
verbose: false,
include: ['*.html', '*.css', '*.js', '*.txt', '*.ttf', '*.atom', '*.stl', '*.xml', '*.svg', '*.eot', '*.json'],
globOptions: { basename: true }
}, hexo.config.minify.gzip)
hexo.config.minify.brotli = Object.assign({
enable: true,
priority: 10,
verbose: false,
include: ['*.html', '*.css', '*.js', '*.txt', '*.ttf', '*.atom', '*.stl', '*.xml', '*.svg', '*.eot', '*.json'],
globOptions: { basename: true }
}, hexo.config.minify.brotli)
hexo.config.minify.zstd = Object.assign({
enable: false,
priority: 10,
verbose: false,
include: ['*.html', '*.css', '*.js', '*.txt', '*.ttf', '*.atom', '*.stl', '*.xml', '*.svg', '*.eot', '*.json'],
globOptions: { basename: true }
}, hexo.config.minify.zstd)
hexo.config.minify.xml = Object.assign({
enable: false,
priority: 10,
verbose: false,
include: ['*.xml', '!*.min.xml'],
removeComments: true,
globOptions: { basename: true }
}, hexo.config.minify.xml)
hexo.config.minify.json = Object.assign({
enable: false,
priority: 10,
verbose: false,
include: ['*.json', '!*.min.json'],
globOptions: { basename: true }
}, hexo.config.minify.json)
if (hexo.config.minify.enable === true && !(hexo.config.minify.previewServer === true && ['s', 'server'].includes(hexo.env.cmd))) {
const filter = require('./lib/filter') const filter = require('./lib/filter')
hexo.extend.filter.register('after_render:html', filter.minifyHtml, hexo.config.minify.html.priority) hexo.extend.filter.register('after_render:html', filter.logicHtml)
hexo.extend.filter.register('after_render:css', filter.minifyCss, hexo.config.minify.css.priority) hexo.extend.filter.register('after_render:css', filter.logicCss)
hexo.extend.filter.register('after_render:js', filter.minifyJs, hexo.config.minify.js.priority) hexo.extend.filter.register('after_render:js', filter.logicJs)
hexo.extend.filter.register('after_generate', filter.minifySvg, hexo.config.minify.svg.priority) hexo.extend.filter.register('after_generate', filter.logicGzip)
hexo.extend.filter.register('after_generate', filter.gzipFn, hexo.config.minify.gzip.priority) hexo.extend.filter.register('after_generate', filter.logicBrotli)
hexo.extend.filter.register('after_generate', filter.brotliFn, hexo.config.minify.brotli.priority)
hexo.extend.filter.register('after_generate', filter.zstdFn, hexo.config.minify.zstd.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)
} }

View File

@ -1,337 +1,189 @@
/* global hexo */
'use strict' 'use strict'
const { minify: htmlMinify } = require('html-minifier-terser')
const CleanCSS = require('clean-css') const CleanCSS = require('clean-css')
const { minify: terserMinify } = require('terser') const Terser = require('terser')
const { optimize: svgOptimize } = require('svgo') const Htmlminifier = require('html-minifier').minify
const zlib = require('node:zlib') const minimatch = require('minimatch')
const { promisify } = require('node:util') const zlib = require('zlib')
const gzip = promisify(zlib.gzip) const br = require('iltorb')
const br = promisify(zlib.brotliCompress)
const { minify: compressXml } = require('minify-xml')
const micromatch = require('micromatch')
const { compress: zstd } = require('@mongodb-js/zstd')
const isMatch = (path = '', patterns = [], options = {}) => { function logicHtml (str, data) {
if (path && patterns) { const hexo = this
if (path.length && patterns.length) { const options = hexo.config.neat_html
if (typeof patterns === 'string') patterns = [patterns] // Return if disabled.
for (const pattern of patterns) { if (options.enable === false) return
// disable basename if a pattern includes a slash
let { basename } = options let path = data.path
// only disable when basename is enabled let exclude = options.exclude
basename = basename && !pattern.includes('/') if (exclude && !Array.isArray(exclude)) exclude = [exclude]
if (micromatch.isMatch(path, pattern, { ...options, basename })) {
return true if (path && exclude && exclude.length) {
} for (let i = 0, len = exclude.length; i < len; i++) {
} if (minimatch(path, exclude[i], { matchBase: true })) return str
} }
} }
return false
let result = Htmlminifier(str, options)
let saved = ((str.length - result.length) / str.length * 100).toFixed(2)
if (options.logger) {
let log = hexo.log || console.log
log.log('Minify the html: %s [%s saved]', path, saved + '%')
}
return result
} }
const match = (paths = [], patterns = [], options = {}) => { function logicCss (str, data) {
let input = paths const hexo = this
if (paths && patterns) { const options = hexo.config.neat_css
if (paths.length && patterns.length) { // Return if disabled.
const output = [] if (options.enable === false) return
if (typeof patterns === 'string') patterns = [patterns]
const exclude = patterns.filter((pattern) => pattern.startsWith('!')) let path = data.path
const include = patterns.filter((pattern) => !pattern.startsWith('!')) let exclude = options.exclude
if (exclude.length) input = micromatch(paths, exclude, options) if (exclude && !Array.isArray(exclude)) exclude = [exclude]
if (include.length) {
for (const pattern of include) { if (path && exclude && exclude.length) {
let { basename } = options for (let i = 0, len = exclude.length; i < len; i++) {
basename = basename && !pattern.includes('/') if (minimatch(path, exclude[i], { matchBase: true })) return str
const tmp = micromatch(input, pattern, { ...options, basename })
if (tmp.length) output.push(...tmp)
}
return [...new Set(output)]
}
return input
} }
} }
return paths
return new Promise(function (resolve, reject) {
new CleanCSS({ level: 2 }).minify(str, function (err, result) {
if (err) return reject(err)
let saved = ((str.length - result.styles.length) / str.length * 100).toFixed(2)
resolve(result.styles)
if (options.logger) {
let log = hexo.log || console.log
log.log('Minify the css: %s [%s saved]', path, saved + '%')
}
})
})
} }
function logFn (original, minified, path, ext) { function logicJs (str, data) {
const saved = ((original.length - minified.length) / original.length * 100).toFixed(2)
const log = this.log || console
log.log(`${ext}: ${path} [${saved}% saved]`)
}
async function minifyHtml (str, data) {
const hexo = this const hexo = this
const options = hexo.config.minify.html const options = hexo.config.neat_js
if (options.enable === false || !str) return // Return if disabled.
if (options.enable === false) return
const { path } = data let path = data.path
const { exclude, globOptions, verbose } = options let exclude = options.exclude
if (exclude && !Array.isArray(exclude)) exclude = [exclude]
// Return if a path matches exclusion pattern if (path && exclude && exclude.length) {
if (isMatch(path, exclude, globOptions)) return str for (let i = 0, len = exclude.length; i < len; i++) {
if (minimatch(path, exclude[i], { matchBase: true })) return str
try { }
const result = await htmlMinify(str, options)
if (verbose) logFn.call(this, str, result, path, 'html')
return result
} catch (err) {
throw new Error(`Path: ${path}\n${err}`)
} }
}
async function minifyCss (str, data) {
const hexo = this
const options = hexo.config.minify.css
if (options.enable === false || !str) return
const { path } = data
const { exclude, globOptions, verbose } = options
if (isMatch(path, exclude, globOptions)) return str
try {
const { styles } = await new CleanCSS(options).minify(str)
if (verbose) logFn.call(this, str, styles, path, 'css')
return styles
} catch (err) {
throw new Error(`Path: ${path}\n${err}`)
}
}
async function minifyJs (str, data) {
const hexo = this
const options = hexo.config.minify.js
if (options.enable === false || !str) return
const { path } = data
const { exclude, globOptions, verbose } = options
if (isMatch(path, exclude, globOptions)) return str
// Terser doesn't like unsupported options // Terser doesn't like unsupported options
const jsOptions = Object.assign({}, options) delete options.enable
delete jsOptions.enable delete options.exclude
delete jsOptions.priority let jsLogger = options.logger
delete jsOptions.verbose delete options.logger
// Old option, retained to avoid crash when upgrading to v4
delete jsOptions.logger
delete jsOptions.exclude
delete jsOptions.globOptions
try { let result = Terser.minify(str, options)
const { code } = await terserMinify(str, jsOptions) let saved = ((str.length - result.code.length) / str.length * 100).toFixed(2)
if (verbose) logFn.call(this, str, code, path, 'js') if (jsLogger) {
return code let log = hexo.log || console.log
} catch (err) { log.log('Minify the js: %s [%s saved]', path, saved + '%')
throw new Error(`Path: ${path}\n${err}`)
} }
return result.code
} }
function minifySvg () { function logicGzip () {
const hexo = this const hexo = this
const options = hexo.config.minify.svg const options = hexo.config.neat_gzip
// Return if disabled.
if (options.enable === false) return if (options.enable === false) return
const { route } = hexo let route = hexo.route
const routeList = route.list() let routeList = route.list()
const { globOptions, include, verbose } = options
// const plugins = Array.isArray(options.plugins) ? extendDefaultPlugins(options.plugins) : extendDefaultPlugins([])
const pluginCfg = Object.prototype.toString.call(options.plugins) === '[object Object]' ? { ...options.plugins } : {}
const plugins = [{
name: 'preset-default',
params: {
overrides: pluginCfg
}
}]
return Promise.all((match(routeList, include, globOptions)).map((path) => { return Promise.all(routeList.filter(path => (path.endsWith('.html') || path.endsWith('.js') || path.endsWith('.css')
|| path.endsWith('.xml') || path.endsWith('.json') || path.endsWith('.txt')
|| path.endsWith('.ttf') || path.endsWith('.atom') || path.endsWith('.stl')
|| path.endsWith('.svg') || path.endsWith('.eot'))).map(path => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const assetPath = route.get(path) // Grab all assets using hexo router
let assetTxt = '' let assetPath = route.get(path)
assetPath.on('data', (chunk) => (assetTxt += chunk))
assetPath.on('end', async () => {
if (assetTxt.length) {
try {
const { data } = svgOptimize(assetTxt, { ...options, plugins })
if (verbose) logFn.call(this, assetTxt, data, path, 'svg')
resolve(route.set(path, data))
} catch (err) {
reject(new Error(`Path: ${path}\n${err}`))
}
}
resolve()
})
})
}))
}
function gzipFn () {
const hexo = this
const options = hexo.config.minify.gzip
if (options.enable === false) return
const { route } = hexo
const routeList = route.list()
const { globOptions, include, verbose } = options
let { level } = options
if (typeof level !== 'number') level = zlib.constants.Z_BEST_COMPRESSION
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', async () => {
if (assetTxt.length) {
try {
const result = await gzip(assetTxt, { level })
if (verbose) logFn.call(this, assetTxt, result, path, 'gzip')
resolve(route.set(path + '.gz', result))
} catch (err) {
reject(new Error(`Path: ${path}\n${err}`))
}
}
resolve()
})
})
}))
}
function brotliFn () {
const hexo = this
const options = hexo.config.minify.brotli
if (options.enable === false) return
const { route } = hexo
const routeList = route.list()
const { globOptions, include, verbose } = options
let { level } = options
if (typeof level !== 'number') level = zlib.constants.BROTLI_MAX_QUALITY
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', async () => {
if (assetTxt.length) {
try {
const result = await br(assetTxt, { params: { [zlib.constants.BROTLI_PARAM_QUALITY]: level } })
if (verbose) logFn.call(this, assetTxt, result, path, 'brotli')
resolve(route.set(path + '.br', result))
} catch (err) {
reject(new Error(`Path: ${path}\n${err}`))
}
}
resolve()
})
})
}))
}
function zstdFn () {
const hexo = this
const options = hexo.config.minify.zstd
if (options.enable === false) return
const { route } = hexo
const routeList = route.list()
const { globOptions, include, verbose } = options
let { level } = options
if (typeof level !== 'number') level = undefined
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', async () => {
if (assetTxt.length) {
try {
const input = Buffer.from(assetTxt, 'utf-8')
const result = await zstd(input, level)
if (verbose) logFn.call(this, assetTxt, result, path, 'zstd')
resolve(route.set(path + '.zst', result))
} catch (err) {
reject(new Error(`Path: ${path}\n${err}`))
}
}
resolve()
})
})
}))
}
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, verbose } = options
return Promise.all((match(routeList, include, globOptions)).map((path) => {
return new Promise((resolve, reject) => {
const assetPath = route.get(path)
let assetTxt = '' let assetTxt = ''
// Extract the content
assetPath.on('data', (chunk) => (assetTxt += chunk)) assetPath.on('data', (chunk) => (assetTxt += chunk))
assetPath.on('end', () => { assetPath.on('end', () => {
if (assetTxt.length) { if (assetTxt.length) {
try { // gzip compress using highest level
const result = compressXml(assetTxt, { ...options }) zlib.gzip(assetTxt, { level: zlib.constants.Z_BEST_COMPRESSION }, (err, Input) => {
if (verbose) logFn.call(this, assetTxt, result, path, 'xml') if (!err) {
resolve(route.set(path, result)) // Save the compressed file to .gz
} catch (err) { route.set(path + '.gz', Input)
reject(new Error(`Path: ${path}\n${err}`)) // Logging
} let saved = ((assetTxt.length - Input.toString().length) / assetTxt.length * 100).toFixed(2)
if (options.logger) {
let log = hexo.log || console.log
log.log('Gzip-compressed %s [%s saved]', path, saved + '%')
}
resolve(assetTxt)
} else {
reject(err)
}
})
} }
resolve()
}) })
}) })
})) }))
} }
function minifyJson () { function logicBrotli () {
const hexo = this const hexo = this
const options = hexo.config.minify.json const options = hexo.config.neat_brotli
// Return if disabled.
if (options.enable === false) return if (options.enable === false) return
const { route } = hexo let route = hexo.route
const routeList = route.list() let routeList = route.list()
const { globOptions, include, verbose } = options
return Promise.all((match(routeList, include, globOptions)).map((path) => { return Promise.all(routeList.filter(path => (path.endsWith('.html') || path.endsWith('.js') || path.endsWith('.css')
|| path.endsWith('.xml') || path.endsWith('.json') || path.endsWith('.txt')
|| path.endsWith('.ttf') || path.endsWith('.atom') || path.endsWith('.stl')
|| path.endsWith('.svg') || path.endsWith('.eot'))).map(path => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const assetPath = route.get(path) // Grab all assets using hexo router
let assetPath = route.get(path)
let assetTxt = '' let assetTxt = ''
// Extract the content
assetPath.on('data', (chunk) => (assetTxt += chunk)) assetPath.on('data', (chunk) => (assetTxt += chunk))
assetPath.on('end', () => { assetPath.on('end', () => {
if (assetTxt.length) { if (assetTxt.length) {
try { // Input has to be buffer for brotli
const result = JSON.stringify(JSON.parse(assetTxt)) let input = new Buffer.from(assetTxt, 'utf-8')
if (verbose) logFn.call(this, assetTxt, result, path, 'json') // brotli defaults to max compression level
resolve(route.set(path, result)) br.compress(input, (err, output) => {
} catch (err) { if (!err) {
reject(new Error(`Path: ${path}\n${err}`)) // Save the compressed file to .br
} route.set(path + '.br', output)
// Logging
let saved = ((input.length - output.toString().length) / input.length * 100).toFixed(2)
if (options.logger) {
let log = hexo.log || console.log
log.log('Brotli-compressed %s [%s saved]', path, saved + '%')
}
resolve(assetTxt)
} else {
reject(err)
}
})
} }
resolve()
}) })
}) })
})) }))
} }
module.exports = { module.exports = {
minifyHtml, logicHtml: logicHtml,
minifyCss, logicCss: logicCss,
minifyJs, logicJs: logicJs,
minifySvg, logicGzip: logicGzip,
gzipFn, logicBrotli: logicBrotli
brotliFn,
zstdFn,
minifyXml,
minifyJson
} }

View File

@ -1,60 +1,38 @@
{ {
"name": "hexo-yam", "name": "hexo-yam",
"description": "Yet Another Minifier. Minify and compress html, js, css, svg, xml and json", "description": "Yet Another Minifier. Minify and compress html, js and css",
"version": "9.0.0", "version": "1.3.1",
"readme": "README.md", "readme": "README.md",
"main": "index.js", "main": "index.js",
"directories": { "directories": {
"lib": "./lib" "lib": "./lib"
}, },
"files": [
"lib/",
"index.js"
],
"scripts": {
"lint": "standard",
"test": "jest"
},
"engines": { "engines": {
"node": ">= 20.9.0" "node": ">= 8"
}, },
"author": "curben", "author": "weyusi",
"license": "MIT", "license": "MIT",
"homepage": "https://github.com/curbengh/hexo-yam", "homepage": "https://github.com/weyusi/hexo-yam",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/curbengh/hexo-yam.git" "url": "https://github.com/weyusi/hexo-yam.git"
}, },
"dependencies": { "dependencies": {
"clean-css": "^5.1.2", "clean-css": "^4.2.1",
"html-minifier-terser": "^7.2.0", "html-minifier": "^4.0.0",
"micromatch": "^4.0.2", "iltorb": "^2.4.2",
"minify-xml": "^3.2.0", "minimatch": "^3.0.4",
"svgo": "^4.0.0", "terser": "^3.17.0"
"terser": "^5.3.0",
"@mongodb-js/zstd": "^2.0.1"
},
"devDependencies": {
"hexo": "^7.1.0",
"jest": "^30.0.4",
"standard": "^17.0.0"
}, },
"keywords": [ "keywords": [
"html",
"js",
"css",
"minify", "minify",
"compress", "compress",
"gzip", "gzip",
"brotli", "brotli",
"zstd",
"hexo-yam", "hexo-yam",
"hexo" "hexo"
], ]
"jest": {
"clearMocks": true,
"collectCoverage": true,
"coverageDirectory": "./coverage/",
"testEnvironment": "node",
"testEnvironmentOptions": {
"globalsCleanup": "off"
}
}
} }

View File

@ -1,258 +0,0 @@
/* eslint-env jest */
'use strict'
const Hexo = require('hexo')
const zlib = require('node:zlib')
const { promisify } = require('node:util')
const brotli = promisify(zlib.brotliCompress)
const unbrotli = promisify(zlib.brotliDecompress)
describe('brotli', () => {
const hexo = new Hexo(__dirname)
const b = require('../lib/filter').brotliFn.bind(hexo)
const path = 'foo.txt'
const input = 'Lorem ipsum dolor sit amet consectetur adipiscing elit fusce'
beforeEach(() => {
hexo.config.minify = {
brotli: {
enable: true,
verbose: false,
include: ['*.html', '*.css', '*.js', '*.txt', '*.ttf', '*.atom', '*.stl', '*.xml', '*.svg', '*.eot', '*.json'],
globOptions: { basename: true }
}
}
hexo.route.set(path, input)
})
afterEach(() => {
const routeList = hexo.route.list()
routeList.forEach((path) => hexo.route.remove(path))
})
test('default', async () => {
await b()
const output = hexo.route.get(path.concat('.br'))
const buf = []
output.on('data', (chunk) => (buf.push(chunk)))
output.on('end', async () => {
const result = Buffer.concat(buf)
const expected = await brotli(input)
const resultUnbr = await unbrotli(result)
const expectedUnbr = await unbrotli(expected)
expect(result.equals(expected)).toBe(true)
expect(resultUnbr.toString()).toBe(input)
expect(expectedUnbr.toString()).toBe(input)
})
})
test('disable', async () => {
hexo.config.minify.brotli.enable = false
const result = await b()
expect(result).toBeUndefined()
})
test('empty file', async () => {
hexo.route.set(path, '')
const routeList = hexo.route.list()
expect(routeList).not.toContain(path.concat('.br'))
const result = await b()
expect(result).toBeDefined()
expect(result[0]).toBeUndefined()
})
test('option', async () => {
const level = 1
hexo.config.minify.brotli.level = level
await b()
const output = hexo.route.get(path.concat('.br'))
const buf = []
output.on('data', (chunk) => (buf.push(chunk)))
output.on('end', async () => {
const result = Buffer.concat(buf)
const expected = await brotli(input, { params: { [zlib.constants.BROTLI_PARAM_QUALITY]: level } })
expect(result.equals(expected)).toBe(true)
})
})
test('option - verbose', async () => {
hexo.config.minify.brotli.verbose = true
hexo.log.log = jest.fn()
await b()
expect(hexo.log.log.mock.calls[0][0]).toContain(`brotli: ${path}`)
})
test('option - level is string', async () => {
const level = 'foo'
hexo.config.minify.brotli.level = level
await b()
const output = hexo.route.get(path.concat('.br'))
const buf = []
output.on('data', (chunk) => (buf.push(chunk)))
output.on('end', async () => {
const result = Buffer.concat(buf)
const expected = await brotli(input, { params: { [zlib.constants.BROTLI_PARAM_QUALITY]: zlib.constants.BROTLI_MAX_QUALITY } })
expect(result.equals(expected)).toBe(true)
})
})
test('include - exclude non-text file by default', async () => {
const path = 'foo.jpg'
hexo.route.set(path, input)
await b()
const result = hexo.route.get(path.concat('.br'))
expect(result).toBeUndefined()
})
test('include - basename', async () => {
hexo.config.minify.brotli.include = 'bar.txt'
const path = 'foo/bar.txt'
hexo.route.set(path, input)
await b()
const result = hexo.route.get(path.concat('.br'))
expect(result).toBeDefined()
})
test('include - slash in pattern', async () => {
hexo.config.minify.brotli.include = '**/lectus/**/*.txt'
const path = 'eleifend/lectus/nullam/dapibus/netus.txt'
hexo.route.set(path, input)
await b()
const result = hexo.route.get(path.concat('.br'))
expect(result).toBeDefined()
})
test('include - basename + slash + basename enabled', async () => {
hexo.route.remove(path)
const paths = [
'lorem/ipsum/dolor.html',
'gravida/sociis/erat/ante.css',
'aptent/elementum.js',
'felis/blandit/cursus.svg'
]
hexo.config.minify.brotli.include = [
'*.html',
'**/sociis/**/*.css'
]
paths.forEach((inpath) => {
hexo.route.set(inpath, input)
})
await b()
const routeList = hexo.route.list()
const expected = [
'lorem/ipsum/dolor.html.br',
'gravida/sociis/erat/ante.css.br'
]
const notExpected = [
'aptent/elementum.js.br',
'felis/blandit/cursus.svg.br'
]
expect(routeList).toEqual(expect.arrayContaining(expected))
expect(routeList).toEqual(expect.not.arrayContaining(notExpected))
})
test('include - basename + slash + basename disabled', async () => {
hexo.route.remove(path)
const paths = [
'lorem/ipsum/dolor.html',
'gravida/sociis/erat/ante.css',
'aptent/elementum.js',
'felis/blandit/cursus.svg'
]
hexo.config.minify.brotli.include = [
'*.html',
'**/sociis/**/*.css'
]
hexo.config.minify.brotli.globOptions = {
basename: false
}
paths.forEach((inpath) => {
hexo.route.set(inpath, input)
})
await b()
const routeList = hexo.route.list()
const expected = [
'gravida/sociis/erat/ante.css.br'
]
const notExpected = [
'lorem/ipsum/dolor.html.br',
'aptent/elementum.js.br',
'felis/blandit/cursus.svg.br'
]
expect(routeList).toEqual(expect.arrayContaining(expected))
expect(routeList).toEqual(expect.not.arrayContaining(notExpected))
})
test('include - reverse pattern + basename disabled', async () => {
hexo.route.remove(path)
const paths = [
'lorem/ipsum/dolor.html',
'gravida/sociis/erat/ante.css',
'aptent/elementum.js',
'felis/blandit/cursus.svg'
]
hexo.config.minify.brotli.include = [
'!dolor.html'
]
hexo.config.minify.brotli.globOptions = {
basename: false
}
paths.forEach((inpath) => {
hexo.route.set(inpath, input)
})
await b()
const routeList = hexo.route.list()
const expected = paths.map((path) => path.concat('.br'))
expect(routeList).toEqual(expect.arrayContaining(expected))
})
test('blns', async () => {
const blns = require('./fixtures/blns.json')
for (const nStr of blns) {
hexo.route.remove(path)
hexo.route.set(path, nStr)
await b()
const output = hexo.route.get(path.concat('.br'))
const buf = []
output.on('data', (chunk) => (buf.push(chunk)))
output.on('end', async () => {
const result = Buffer.concat(buf)
const resultUnbr = await unbrotli(result)
expect(resultUnbr.toString()).toBe(nStr)
})
}
})
})

View File

@ -1,107 +0,0 @@
/* eslint-env jest */
'use strict'
const Hexo = require('hexo')
const CleanCSS = require('clean-css')
describe('css', () => {
const hexo = new Hexo(__dirname)
const c = require('../lib/filter').minifyCss.bind(hexo)
const input = 'foo { bar: baz; } foo { aaa: bbb; }'
const path = 'foo.css'
beforeEach(() => {
hexo.config.minify = {
css: {
enable: true,
verbose: false,
exclude: ['*.min.css'],
level: 2,
globOptions: { basename: true }
}
}
})
test('default', async () => {
const result = await c(input, { path })
const { styles } = await new CleanCSS(hexo.config.minify.css).minify(input)
expect(result).toBe(styles)
})
test('disable', async () => {
hexo.config.minify.css.enable = false
const result = await c(input, { path })
expect(result).toBeUndefined()
})
test('empty file', async () => {
const result = await c('', { path })
expect(result).toBeUndefined()
})
test('option', async () => {
const customOpt = {
level: {
1: {
mergeAdjacentRules: false
}
}
}
hexo.config.minify.css = customOpt
const result = await c(input, { path })
const { styles } = await new CleanCSS(customOpt).minify(input)
expect(result).toBe(styles)
})
test('option - verbose', async () => {
hexo.config.minify.css.verbose = true
hexo.log.log = jest.fn()
await c(input, { path })
expect(hexo.log.log.mock.calls[0][0]).toContain(`css: ${path}`)
})
test('option - invalid', async () => {
const customOpt = {
level: 9000
}
hexo.config.minify.css = customOpt
let expected
try {
await new CleanCSS(customOpt).minify(input)
} catch (err) {
expected = err
}
expect(expected).toBeDefined()
await expect(c(input, { path })).rejects.toThrow(`Path: ${path}\n${expected}`)
})
test('exclude - *.min.css', async () => {
const result = await c(input, { path: 'foo/bar.min.css' })
expect(result).toBe(input)
})
test('exclude - basename', async () => {
const exclude = '*baz.css'
hexo.config.minify.css.exclude = exclude
const result = await c(input, { path: 'foo/barbaz.css' })
expect(result).toBe(input)
})
test('exclude - slash in pattern', async () => {
const exclude = '**/lectus/**/*.css'
hexo.config.minify.css.exclude = exclude
const result = await c(input, { path: 'eleifend/lectus/nullam/dapibus/netus.css' })
expect(result).toBe(input)
})
})

View File

@ -1,516 +0,0 @@
[
"undefined",
"undef",
"null",
"NULL",
"(null)",
"nil",
"NIL",
"true",
"false",
"True",
"False",
"TRUE",
"FALSE",
"None",
"hasOwnProperty",
"then",
"\\",
"\\\\",
"0",
"1",
"1.00",
"$1.00",
"1/2",
"1E2",
"1E02",
"1E+02",
"-1",
"-1.00",
"-$1.00",
"-1/2",
"-1E2",
"-1E02",
"-1E+02",
"1/0",
"0/0",
"-2147483648/-1",
"-9223372036854775808/-1",
"-0",
"-0.0",
"+0",
"+0.0",
"0.00",
"0..0",
".",
"0.0.0",
"0,00",
"0,,0",
",",
"0,0,0",
"0.0/0",
"1.0/0.0",
"0.0/0.0",
"1,0/0,0",
"0,0/0,0",
"--1",
"-",
"-.",
"-,",
"999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999",
"NaN",
"Infinity",
"-Infinity",
"INF",
"1#INF",
"-1#IND",
"1#QNAN",
"1#SNAN",
"1#IND",
"0x0",
"0xffffffff",
"0xffffffffffffffff",
"0xabad1dea",
"123456789012345678901234567890123456789",
"1,000.00",
"1 000.00",
"1'000.00",
"1,000,000.00",
"1 000 000.00",
"1'000'000.00",
"1.000,00",
"1 000,00",
"1'000,00",
"1.000.000,00",
"1 000 000,00",
"1'000'000,00",
"01000",
"08",
"09",
"2.2250738585072011e-308",
",./;'[]\\-=",
"<>?:\"{}|_+",
"!@#$%^&*()`~",
"\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f",
"ᅡタᅡチᅡツᅡテᅡトᅡニᅡヌᅡネᅡノᅡハᅡヒᅡフᅡヘᅡホᅡマᅡミᅡムᅡメᅡモᅡヤᅡユᅡヨᅡラᅡリᅡルᅡレᅡロᅡワᅡンᅡ゙ᅡ゚",
"\t\u000b\f ᅡナ £レタ¬タツ¬タテ¬タツ¬タテ¬タト¬タナ¬タニ¬タヌ¬タネ¬タノ¬タハ¬タヒ¬タᄄ¬タᄅ¬タᆵ¬チ゚ ̄タタ",
"ᅡᆳ￘タ￘チ￘ツ￘テ￘ト￘ナ￘ワᅴンᅵマ£ᅠホ¬タヒ¬タフ¬タヘ¬タホ¬タマ¬タᆰ¬タᆱ¬タᆲ¬タᆳ¬タᆴ¬チᅠ¬チᄀ¬チᄁ¬チᆪ¬チᄂ¬チᆭ¬チᄃ¬チᄄ¬チᄅ¬チᆰ¬チᆱ¬チᆲ¬チᆳ¬チᆴ¬チᆵ￯ᄏ﾿￯﾿ᄍ￯﾿ᄎ￯﾿ᄏ￰ムツᄑ￰ロᄇᅠ￰ロᄇᄀ￰ロᄇᄁ￰ロᄇᆪ￰ンナᄈ￰ンナᄡ￰ンナᄉ￰ンナᄊ￰ンナᄋ￰ンナᄌ￰ンナᄍ￰ンナᄎ￳ᅠタチ￳ᅠタᅠ￳ᅠタᄀ￳ᅠタᄁ￳ᅠタᆪ￳ᅠタᄂ￳ᅠタᆬ￳ᅠタᆭ￳ᅠタᄃ￳ᅠタᄄ￳ᅠタᄅ￳ᅠタᆰ￳ᅠタᆱ￳ᅠタᆲ￳ᅠタᆳ￳ᅠタᆴ￳ᅠタᆵ￳ᅠタᄚ￳ᅠタᄆ￳ᅠタᄇ￳ᅠタᄈ￳ᅠタᄡ￳ᅠタᄉ￳ᅠタᄊ￳ᅠタᄋ￳ᅠタᄌ￳ᅠタᄍ￳ᅠタᄎ￳ᅠタᄏ￳ᅠタᄐ￳ᅠタᄑ￳ᅠタᄒ￳ᅠタ﾿￳ᅠチタ￳ᅠチチ￳ᅠチツ￳ᅠチテ￳ᅠチト￳ᅠチナ￳ᅠチニ￳ᅠチヌ￳ᅠチネ￳ᅠチノ￳ᅠチハ￳ᅠチヒ￳ᅠチフ￳ᅠチヘ￳ᅠチホ￳ᅠチマ￳ᅠチミ￳ᅠチム￳ᅠチメ￳ᅠチモ￳ᅠチヤ￳ᅠチユ￳ᅠチヨ￳ᅠチラ￳ᅠチリ￳ᅠチル￳ᅠチレ￳ᅠチロ￳ᅠチワ￳ᅠチン￳ᅠヂ￳ᅠチ゚￳ᅠチᅠ￳ᅠチᄀ￳ᅠチᄁ￳ᅠチᆪ￳ᅠチᄂ￳ᅠチᆬ￳ᅠチᆭ￳ᅠチᄃ￳ᅠチᄄ￳ᅠチᄅ￳ᅠチᆰ￳ᅠチᆱ￳ᅠチᆲ￳ᅠチᆳ￳ᅠチᆴ￳ᅠチᆵ￳ᅠチᄚ￳ᅠチᄆ￳ᅠチᄇ￳ᅠチᄈ￳ᅠチᄡ￳ᅠチᄉ￳ᅠチᄊ￳ᅠチᄋ￳ᅠチᄌ￳ᅠチᄍ￳ᅠチᄎ￳ᅠチᄏ￳ᅠチᄐ￳ᅠチᄑ￳ᅠチᄒ￳ᅠチ﾿",
"￯ᄏ﾿",
"￯﾿ᄒ",
"ᅫᄅ¬ノネᅢᄃ¬ネレ¬ネᆱᅨワᅡᄉ¬ノᄂ¬ノᆬᅢᄋ",
"ᅢᆬᅢ゚¬ネツᅥメᅡ례ル¬ネニᅨレᅡᆲ¬タᆭᅢᆭ",
"ᅤモ¬ネムᅡᄡᅡᆴ¬タᅠᅡᆬᅡ뗴ニᅢ죄タ¬タワ¬タリ",
"ᅡᄀ¬ト깏ᅡᄁ¬ネ゙ᅡ다ᄊ¬タ깕ᅡᄎ¬タモ¬ノᅠ",
"ᅡ졔ロᅢヌ¬ラハᅣ몌ワᅢツᅡᆵᅨリᅡ﾿",
"ᅢナᅢヘᅢホᅢマᅨンᅢモᅢヤ￯ᆪ﾿ᅢメᅢレᅢニ¬リテ",
"ᅤメ¬ダᅡᄡ¬タᄚᅨヌᅢチᅡ뗴ニᅢリ¬ネマ¬タン¬タル",
"`¬チト¬ツᆲ¬タᄍ¬タᄎ￯ᆲチ￯ᆲツ¬タ가ᄚᅡᄋ¬タレ¬タヤᅡᄆ",
"¬ナロ¬ナワ¬ナン¬ナ゙",
"￐チ￐ツ￐テ￐ト￐ナ￐ニ￐ヌ￐ネ￐ノ￐ハ￐ヒ￐フ￐ヘ￐ホ￐マ￐ミ￐ム￐メ￐モ￐ヤ￐ユ￐ヨ￐ラ￐リ￐ル￐レ￐ロ￐ワ￐ン￐゙￐゚￐ᅠ￐ᄀ￐ᄁ￐ᆪ￐ᄂ￐ᆬ￐ᆭ￐ᄃ￐ᄄ￐ᄅ￐ᆰ￐ᆱ￐ᆲ￐ᆳ￐ᆴ￐ᆵ￐ᄚ￐ᄆ￐ᄇ￐ᄈ￐ᄡ￐ᄉ￐ᄊ￐ᄋ￐ᄌ￐ᄍ￐ᄎ￐ᄏ￐ᄐ￐ᄑ￐ᄒ￐﾿￑タ￑チ￑ツ￑テ￑ト￑ナ￑ニ￑ヌ￑ネ￑ノ￑ハ￑ヒ￑フ￑ヘ￑ホ￑マ",
"￙ᅠ￙ᄀ￙ᄁ￙ᆪ￙ᄂ￙ᆬ￙ᆭ￙ᄃ￙ᄄ￙ᄅ",
"¬チᄚ¬チᄡ¬チᄉ",
"¬ツタ¬ツチ¬ツツ",
"¬チᄚ¬チᄡ¬チᄉ¬ツタ¬ツチ¬ツツ",
"¢ᄌヤ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ ¢ᄌヤ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ ¢ᄌヤ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍノ¢ᄍヌ¢ᄍヌ¢ᄍヌ¢ᄍヌ",
"'",
"\"",
"''",
"\"\"",
"'\"'",
"\"''''\"'\"",
"\"'\"'\"''''\"",
"<foo val=¬タワbar¬タン />",
"<foo val=¬タワbar¬タン />",
"<foo val=¬タンbar¬タワ />",
"<foo val=`bar' />",
"￧ヤᄚ¦ᄌᆳ ̄チユ ̄ツモ ̄チᆱ ̄チツ ̄チメ ̄チᆭ¦ᄌヒ ̄チユ ̄チト",
" ̄テム ̄テᄐ ̄テニ ̄ツᆪ ̄テᄐ ̄チᄌ│ᄀフ ̄チヒ ̄チᆰ ̄チト ̄チヒ",
"¥メフ│ᆪᄑ₩ᄐᄁ│ᆰ゙",
"←テᄄ│ミᄑ₩ᅠᄐ",
"↓ツᆲ■レフ↑ᄈᄐ■ユル↓ロミ ↓ヨᄡ■ユル↓ラᄚ↑ᄉᆲ↓ニフ",
"↓ᄚᆭ↓ᄚᄄ→ᆬᄐ ■テタ↑ᄈᅠ ↓リᄄ ■ホᄇ↓ヒワ→ᄃᄄ↑ᄈᄐ ↓ムロ→ヒᄂ→ᆭᆲ →リᅠ→ᄚᄅ↑ᄚチ■ユリ",
"￧ᄂᄒ₩ワテ￧ᄃム¥ᆳᄌ←ルᄁ│ᆰ゙¥ᆳᄌ￧ᅠヤ￧ᄅᄊ₩ノタ",
"↓レᄌ→゙タ→ᄚヤ■ニᅠ→ᆬᄡ",
"￰ᅠワホ￰ᅠワᄆ￰ᅠンᄍ￰ᅠᄆモ￰ᅠᄆᄌ￰ᅠᄇヨ￰ᅠᄈマ",
"￰ミミワ ￰ミミヤ￰ミミヌ￰ミミン￰ミミタ￰ミミᄀ￰ミミヌ￰ミミモ ￰ミミル￰ミミハ￰ミミᄀ￰ミミン￰ミミモ/￰ミミン￰ミミヌ￰ミミラ￰ミミハ￰ミミᄂ￰ミミヤ ￰ミミメ￰ミミヒ￰ミミラ ￰ミミメ￰ミミフ ￰ミミワ ￰ミミᄀ￰ミミタ￰ミミヨ￰ミミヌ￰ミミᄂ￰ミミモ￰ミミン ￰ミミᄆ￰ミムツ ￰ミムト ￰ミミヤ￰ミミヌ￰ミミン￰ミミタ￰ミミᄀ￰ミミヌ￰ミミモ ￰ミミマ￰ミミニ￰ミミナ￰ミミᄂ￰ミミニ￰ミミレ￰ミミハ￰ミミᄀ￰ミミン￰ミミニ￰ミミモ￰ミミニ",
"ᄀᄄ ̄テン ̄チツA←ᄋラᅤメᅢᄅ￯ᄐᄁ←タヘᅢワᅢ゚ᅡᆰᅣナᅢᄆ¦ᄌツ ̄ミタタタ",
"￈ᄎ",
"￈ᄒ",
" ̄テᄑ¢ᄐᄐ¢ᄎネ￙トᅪワ¢ᄎネ¢ᄐᄑ￯ᄒノ  ̄テᄑ¢ᄐᄐ¢ᄎネ￙トᅪワ¢ᄎネ¢ᄐᄑ￯ᄒノ",
"(￯ᄑᄀ¬ラユ ¬ネタ ¬ラユ￯ᄑᄀ)",
"￯ᄑタ￯ᄑᄄ(ᅡᄡ¬ネタ￯ᄑタ¬ネᄅ",
"__￯ᄒロ(,_,*)",
" ̄テᄏ(￯﾿ᆪ¬ネタ￯﾿ᆪ) ̄テᄏ:*:",
"￯ᄒ゚￯ᄑᆬ¬ワ﾿ ̄テᄒ¬ユᄇ(￯ᄑᄀ¬ラユ¬タ﾿¬ラユ￯ᄑᄀ)¬ユᄆ¬ワ﾿￯ᄑᆬ￯ᄒ゚",
", ̄タツ ̄テᄏ:*: ̄テᄏ ̄ツワ¬タル( ¬リᄏ ᅬノ ¬リᄏ ) ̄タツ ̄テᄏ:*: ̄テᄏ ̄ツワ¬タル",
"(¬ユᆵᅡᄚ¬ヨ가ᄚ￯ᄐノ¬ユᆵ￯ᄌᄉ ¬ヤᄏ¬ヤチ¬ヤᄏ)",
"(￯ᄒノ¢ᄇᆬ￧ロハ¢ᄇᆬ￯ᄐノ￯ᄒノ￯ᄏ﾿ ¬ヤᄏ¬ヤチ¬ヤᄏ",
"¬ヤᆲ¬ヤタ¬ヤᆲ ̄テホ( ᅡᄎ _ ᅡᄎ ̄テホ)",
"( ᅪ가ᄚ ᅪワᅧヨ ᅪ가ᄚ)",
"ᅡᆵ\\_( ̄テト)_/ᅡᆵ",
"￰゚リヘ",
"￰゚ムᄅ￰゚マᄑ",
"￰゚ムᄄ¬タヘ￰゚ᆭᄚ ￰゚ムᄄ￰゚マ﾿¬タヘ￰゚ᆭᄚ ￰゚ムᄄ¬タヘ￰゚ᆭᄆ ￰゚ムᄄ￰゚マ﾿¬タヘ￰゚ᆭᄆ ￰゚ᆭᄍ￰゚マ﾿¬タヘ¬ルツ￯ᄌマ",
"￰゚ムᄒ ￰゚ルヌ ￰゚メチ ￰゚ルナ ￰゚ルニ ￰゚ルヒ ￰゚ルホ ￰゚ルヘ",
"￰゚ミᄉ ￰゚ルネ ￰゚ルノ ￰゚ルハ",
"¬ンᄂ￯ᄌマ ￰゚メヤ ￰゚メフ ￰゚メユ ￰゚メ゙ ￰゚メモ ￰゚メラ ￰゚メヨ ￰゚メリ ￰゚メン ￰゚メ゚ ￰゚メワ ￰゚メロ ￰゚メレ ￰゚メル",
"¬ワヒ￰゚マ﾿ ￰゚メᆰ￰゚マ﾿ ￰゚ムミ￰゚マ﾿ ￰゚ルフ￰゚マ﾿ ￰゚ムマ￰゚マ﾿ ￰゚ルマ￰゚マ﾿",
"￰゚ムᄄ¬タヘ￰゚ムᄅ¬タヘ￰゚ムᆭ ￰゚ムᄄ¬タヘ￰゚ムᄅ¬タヘ￰゚ムᄃ¬タヘ￰゚ムᆭ ￰゚ムᄄ¬タヘ￰゚ムᄄ¬タヘ￰゚ムᆭ ￰゚ムᄅ¬タヘ￰゚ムᄅ¬タヘ￰゚ムᄃ ￰゚ムᄄ¬タヘ￰゚ムᆭ ￰゚ムᄄ¬タヘ￰゚ムᄃ¬タヘ￰゚ムᆭ ￰゚ムᄅ¬タヘ￰゚ムᆭ ￰゚ムᄅ¬タヘ￰゚ムᄃ¬タヘ￰゚ムᆭ",
"￰゚レᄒ ￰゚ニメ ￰゚ニモ ￰゚ニユ ￰゚ニヨ ￰゚ニラ ￰゚ニル ￰゚マᄃ",
"0￯ᄌマ¬テᆪ 1￯ᄌマ¬テᆪ 2￯ᄌマ¬テᆪ 3￯ᄌマ¬テᆪ 4￯ᄌマ¬テᆪ 5￯ᄌマ¬テᆪ 6￯ᄌマ¬テᆪ 7￯ᄌマ¬テᆪ 8￯ᄌマ¬テᆪ 9￯ᄌマ¬テᆪ ￰゚ヤ゚",
"￰゚ヌᄎ￰゚ヌᄌ￰゚ヌᄋ￰゚ヌᄎ￰゚ヌᄌ ￰゚ヌᆭ￰゚ヌᆱ￰゚ヌᆭ￰゚ヌᄇ￰゚ヌᄌ",
"￰゚ヌᄎ￰゚ヌᄌ￰゚ヌᄋ￰゚ヌᄎ￰゚ヌᄌ￰゚ヌᆭ￰゚ヌᆱ￰゚ヌᆭ￰゚ヌᄇ",
"￰゚ヌᄎ￰゚ヌᄌ￰゚ヌᄋ￰゚ヌᄎ￰゚ヌᄌ￰゚ヌᆭ",
"￯ᄐム￯ᄐメ￯ᄐモ",
"￙ᄀ￙ᄁ￙ᆪ",
"￘ᆱ￙ナ ￙ニ￙チ￘ᄈ ￘ᄈ￙ツ￘ᄋ￘ᆰ ￙ネ￘ᄄ￘ᄃ￙ト￘ᆰ￘ᆳ￘ᆵ￙ハ￘ᆵ￘フ, ￘ᆲ￘ᄇ￙ハ￘ᄆ￘ᆰ￙ハ ￘ᄄ￘ᄃ￘ᄈ￘ᆰ￘ᆴ￘ᆵ￘ᄃ￙ナ ￘ᆪ￙ニ ￘ᆵ￙ニ￙ネ. ￘ᆬ￘ᄚ ￙ヌ￙ニ￘ᄃ￘゚ ￘ᄃ￙ト￘ᄈ￘ᆰ￘ᄃ￘ᄆ ￙ネ￘ᆰ￙ニ￘ᄉ￙ハ￘ᄄ ￙テ￘ᄃ￙ニ. ￘ᆪ￙ヌ￙ム￙ト ￘ᄃ￙ハ￘ᄋ￘ᄃ￙ト￙ハ￘ᄃ￘フ ￘ᄄ￘ᄆ￙ハ￘ᄋ￘ᄃ￙ニ￙ハ￘ᄃ-￙チ￘ᄆ￙ニ￘ᄈ￘ᄃ ￙ツ￘ᆵ ￘ᆪ￘ᆴ￘ᄚ. ￘ᄈ￙ト￙ハ￙ナ￘ᄃ￙ニ￘フ ￘ᆬ￘ᆰ￙チ￘ᄃ￙ツ￙ハ￘ᄅ ￘ᄄ￙ハ￙ニ ￙ナ￘ᄃ, ￙ハ￘ᄚ￙テ￘ᄆ ￘ᄃ￙ト￘ᆳ￘ᆵ￙ネ￘ᆵ ￘ᆪ￙ハ ￘ᄄ￘ᄍ￘ᆵ, ￙ナ￘ᄍ￘ᄃ￙ナ￙ト￘ᄅ ￘ᄄ￙ネ￙ト￙ニ￘ᆵ￘ᄃ￘フ ￘ᄃ￙ト￘ᆬ￘ᄋ￙ト￘ᄃ￙ツ ￘ᄍ￙ト ￘ᆬ￙ハ￙ネ.",
"ᅲムᅱᄚᅱ튜뛰슈ミᅲ뤼ᄡᅲチᅲルᅲᆰ, ᅲムᅱ쥐튜뛰쥬ミ ᅲミᅱ뮤ワᅱ쮸ヤᅱᄡᅲルᅲン, ᅲミᅱ슑 ᅲヤᅱ유뤼쥐튜チᅲ゙ᅱ유ルᅱᄡᅲン, ᅲユᅱᄚᅲミᅱ슑 ᅲヤᅱ쥬ミᅱ쥬뛰쓙",
"ᅲヤᅱ쥬ルᅱᄚᅲᆰᅱ쥬ヤtest￘ᄃ￙ト￘ᄉ￙チ￘ᆳ￘ᄃ￘ᆰ ￘ᄃ￙ト￘ᆰ￙ム￘ᆳ￙ネ￙ト",
"￯ᄋᄑ",
"￯ᄋᄎ",
"￙ナ￙マ￙ニ￙ホ￘ᄃ￙ツ￙ホ￘ᄡ￙ホ￘ᄅ￙マ ￘ᄈ￙マ￘ᄄ￙マ￙ト￙ミ ￘ᄃ￙ミ￘ᄈ￙メ￘ᆰ￙ミ￘ᆴ￙メ￘ᆵ￙ホ￘ᄃ￙ナ￙ミ ￘ᄃ￙ト￙ト￙マ￙ム￘ᄎ￙ホ￘ᄅ￙ミ ￙チ￙ミ￙ハ ￘ᄃ￙ト￙ニ￙マ￙ム￘ᄌ￙マ￙ナ￙ミ ￘ᄃ￙ト￙メ￙ツ￙ホ￘ᄃ￘ᆭ￙ミ￙ナ￙ホ￘ᄅ￙ミ ￙ネ￙ホ￙チ￙ミ￙ハ￙ナ ￙ハ￙ホ￘ᆴ￙マ￘ᄉ￙ホ￙ム ￘ᄃ￙ト￘ᆰ￙ホ￙ム￘ᄋ￙メ￘ᄄ￙ミ￙ハ￙ツ￙ホ￘ᄃ￘ᆰ￙マ ￘ᄃ￙ト￙メ￘ᆳ￘ᄃ￘ᄈ￙マ￙ネ￘ᄄ￙ミ￙ハ￙ホ￙ム￘ᄅ￙マ￘フ ",
"£レロ£レト£レモ£レミ£レヒ£レメ£レト£レタ£レム£レト£レツ£レム£レマ£レナ£レワ¬タᆰ¬タᆰ¬タᆰ",
"¬タᆰ¬タᆰ£レロ£レタ£レタ£レタ£レタ£レタ£レタ£レタ£レタ£レタ£レタ£レタ£レタ£レタ£レタ£レタ£レタ£レタ£レワ¬タᆰ",
"¬タᆰ¬タᆰtest¬タᆰ",
"¬タᆱtest¬タᆱ",
"¬タᄅtest¬タᄅ",
"test¬チtest¬タᆱ",
"¬チᆭtest¬チᄃ",
"£ᄍᄚᅩ초초ユoᅪ゙ ᅩᄋiᅩ볿ᅪヌᅩᆰᅪルnᅩンᅩラᅪユvᅩ゚ᅩワᅩリᅩᆭᅪ゚oᅩ쏘ルᅩᄚᅩkᅢ똬レᅩᆴᅩ촑ᅩ쪼모ᄂ ᅩヨtᅩンᅪユᅩ뽃ᅩ콝ᅪ゙hᅩ톼モᅩ볺ᅩ뽀リᅩᄇeᅪヌᅩᆪᅩᄚᅩᆭᅩᆲᅪホ ᅩ꼬토코모リhᅪレᅪホᅪルᅩワᅩᆪᅩ봐ナiᅩᆭᅩ볷ᅩᄚᅩᄂvᅩ콰ヘeᅩ촔ᅩ뽉ᅩᄚ-mᅩᄁiᅪナnᅩヨᅩ초゙ᅩ봂ᅩᄚdᅩ소토゚ᅪルᅩ로토リᅩᄈ ᅩ゙ᅩᆬᅩ모뽌rᅩロᅩラᅩリeᅪルpᅪrᅩ토゙ᅩ콠ᅩラeᅩ초ᅩᆪᅪ゚sᅩリᅪヌᅩ뽜ヘᅩンᅪノeᅪノᅩᆬᅩᆵᅩ゙ᅩ봐レᅩᆲᅪワᅦ쫇ᅪホᅪホᅩ゚ᅩヨᅪヌᅩᄂtᅪヘᅩᆲᅩ놔モᅩ톬ᅪリᅪナiᅩᆰᅩᄆnᅪgᅩᄡᅪノ ᅪマᅪノᅪナcᅩᆲᅩ゚hᅪᄀaᅩᆱᅩ콢ᅪリoᅩᆱᅩ゚ᅩヨᅪヘᅩルᅩンᅪノsᅩラᅩᆭᅩᄇ.ᅩ또쫘ネᅩᆪ",
"ᅩ과モᅩ゙ᅪナIᅩラᅩリᅩᆭᅪンnᅪヌᅪヌᅪルvᅩᆴᅩᆱokᅩ볾ᅩルᅪネiᅩヨᅪルᅩᆳᅩ쪼ᅩ゙nᅩ고콡ᅩᆪᅩᄎgᅩ봐ネᅪルᅩᆳᅪルᅩᆲᅪホ ᅩᄚtᅪヤᅩᆭhᅩ゙ᅩᄇeᅩ꼬ᄂ ᅪヘᅩᆲᅩ봐ヨfᅩᄡᅩリᅪユᅩᆪᅢ똬ヨ£ᄎ쫁ᅩᄅlᅪヨᅪヤᅪレiᅪモᅪレᅩᆭᅪnᅪヨᅪヘᅩラᅪモᅩ뽍gᅪヘ ᅩᄄoᅪレᅩᆰᅪᄀfᅩリᅩᆪᅩᆲ ᅩヨᅩリᅪヨᅩ゚ᅪルᅩᆴcᅭノᅪヤᅩᆱᅪヨᅪモᅪヌᅪヨᅪナhᅩ소녻ᅪレᅪヤᅢ고ラᅩ톼ユᅪナoᅩ톣ᅩᆬsᅩ뫄ネᅩ초ヨᅩᆭᅩ콰ᄁ.ᅩロᅩヨᅩ゙ᅩᅠᅩᆱᅩᄚ",
"ᅩラᅩ촤ヨᅩ쫊ᅪモ£ᄍᆴᅩ놔ヘᅩᆬᅪヌᅪネhᅩ보チeᅪマᅪモᅩ토ラᅩルᅩ톣ᅪヤ ᅪヌᅩワᅩ모ᅪモᅪヘᅪナNᅪユᅪeᅩラᅩᄆzᅩリᅩンᅩワᅩ촤ルpᅩ노초쫘ヘᅩᆵᅪレeᅩᅩ코ᅪワrᅩ또놔ヘᅩ초ヨᅪヤᅩヨᅩヨdᅩᅩ゚ᅩᆳᅩᆲᅩンᅪ゚iᅩᆭᅪヨᅩ롸モᅪヤᅩᄂaᅩᅩラᅩᆲᅪノᅩルnᅪレᅪワ ᅩ코゙ᅩᄚᅪレᅪナhᅩ솨ノiᅩ뽀゙vᅩ꽈ヌ£ᄌルᅪホᅪ゚-ᅭノᅩᆳᅩ로톼ヤmᅩ놄ᅩᆱiᅪユᅪヌᅩンᅩᆭnᅩラᅪル£ᄌヘᅩ゚ ᅩᆵᅩ봐ユᅪ゙ᅦᆱᅩ゚ᅩᆵᅩᄚᅩ봐ルᅩ코ンf ᅩᆰᅩᄚᅩᄚᅩラᅩヨᅩᆳᅩリᅪリcᅩᆭᅪヘᅩ보゙ᅪヘᅩ로ル£ᄌᆬᅪレaᅩᆴᅪホᅩ゚ᅩルᅪワᅥ고로쫘ホsᅩᄂ.ᅩンᅩン ᅭノZᅩ고ヨᅩワᅪヨᅩᄚᅩᆪᅪノᅩワaᅪヨᅩᄚᅪルᅩᆲᅪᄀlᅩ볾ᅩ뽜ヘᅩᄅgᅩ고゚ᅩ토뫄レᅩ゙ᅩᆲᅪナoᅩラᅪワ.ᅩ゚",
"ᅩᆭHᅩᆲᅩ노ラᅩ놔ンeᅪワ ᅩワᅩᆬᅩンᅩ콰ヘᅩ゚ᅩチwᅩユhᅩヨᅩᆵᅪモoᅩンᅪルᅩヨᅪホᅩ몵 ᅭノᅩ초ルᅩ゙ᅩ゚ᅪネWᅩ오톬aᅩ촑ᅪヘᅣᆵᅪネᅪユᅩᆳᅪルᅩᆵᅩワtᅩ쏘톭sᅩリᅪルᅪヨᅩユ ᅩᅩᆱᅩBᅩ콰ヘᅪルᅪノᅩ뽜ナeᅩᄉhᅩ솗ᅪヌᅩᆱᅪルiᅩ쫘モᅩ뽀뽍ᅪホᅩᆱᅩユnᅪ゚dᅩᄡᅩᆰᅩワᅩヨ ᅩᄚᅪノᅩ롸ヌᅪルᅩ봐゙ᅪナTᅪヨᅩ톼モᅩᆰᅪᄁhᅪマᅪモᅩᆴᅩᄏeᅩᆲᅩンᅩ゚ᅪナ ᅩ노쪼ンWᅪルᅩ゙ᅩンᅪヤᅪヌᅪンᅪナaᅪマᅪモᅪヤᅩ쪼톣lᅩᄡᅪヤᅩᄚᅩ노゚ᅪヤ£ᄌ폶.ᅪユ",
"Zᅩᆴᅩ゙ᅩᅪルᅪヤᅪナ£ᄌタᅩラᅩ゙ᅪネᅩ코ラ£ᄌ쏴ルᅪホᅩᆵᅩ쪼゙ᅪモGᅩᄏOᅩᆳᅩラᅩᆴ",
"ᅨル￉ミnb£ᄡノl￉ミ ￉ミuᅥテ￉ミ￉ᆵ ᅦン￉ᄍolop ᅧヌᅦン ᅦン￉ᄍoq￉ミl ᅧヌn ᅧヌunp£ᄡノp£ᄡノ￉ヤu£ᄡノ ￉ᄍod￉ᆵᅦンᅧヌ po￉ᆵsn£ᄡノᅦン op pᅦンs 'ᅧヌ£ᄡノlᅦン ᅥテu£ᄡノ￉ヤs£ᄡノd£ᄡノp￉ミ ￉ᄍnᅧヌᅦンᅧヌ￉ヤᅦンsuo￉ヤ 'ᅧヌᅦン￉ᆵ￉ミ ᅧヌ£ᄡノs ￉ᄍolop ￉ᆵnsd£ᄡノ ￉ᆵᅦン￉ᄍoᅨᆬ",
"00ᅨルᅥヨ$-",
"￯ᄐᄡ￯ᄑネ￯ᄑナ ￯ᄑム￯ᄑユ￯ᄑノ￯ᄑテ￯ᄑヒ ￯ᄑツ￯ᄑメ￯ᄑマ￯ᄑラ￯ᄑホ ￯ᄑニ￯ᄑマ￯ᄑリ ￯ᄑハ￯ᄑユ￯ᄑヘ￯ᄑミ￯ᄑモ ￯ᄑマ￯ᄑヨ￯ᄑナ￯ᄑメ ￯ᄑヤ￯ᄑネ￯ᄑナ ￯ᄑフ￯ᄑチ￯ᄑレ￯ᄑル ￯ᄑト￯ᄑマ￯ᄑヌ",
"￰ンミモ￰ンミᄀ￰ンミ゙ ￰ンミᆰ￰ンミᆴ￰ンミᄁ￰ンミワ￰ンミᄂ ￰ンミロ￰ンミᆱ￰ンミᄄ￰ンミᄚ￰ンミᄃ ￰ンミ゚￰ンミᄄ￰ンミᄆ ￰ンミᆪ￰ンミᆴ￰ンミᆭ￰ンミᄅ￰ンミᆲ ￰ンミᄄ￰ンミᆵ￰ンミ゙￰ンミᆱ ￰ンミᆳ￰ンミᄀ￰ンミ゙ ￰ンミᆬ￰ンミレ￰ンミᄈ￰ンミᄇ ￰ンミン￰ンミᄄ￰ンミᅠ",
"￰ンユ﾿￰ンヨヘ￰ンヨハ ￰ンヨヨ￰ンヨレ￰ンヨホ￰ンヨネ￰ンヨミ ￰ンヨヌ￰ンヨラ￰ンヨヤ￰ンヨワ￰ンヨモ ￰ンヨヒ￰ンヨヤ￰ンヨン ￰ンヨマ￰ンヨレ￰ンヨメ￰ンヨユ￰ンヨリ ￰ンヨヤ￰ンヨロ￰ンヨハ￰ンヨラ ￰ンヨル￰ンヨヘ￰ンヨハ ￰ンヨム￰ンヨニ￰ンヨ゚￰ンヨ゙ ￰ンヨノ￰ンヨヤ￰ンヨフ",
"￰ンムᄏ￰ンメノ￰ンメニ ￰ンメメ￰ンメヨ￰ンメハ￰ンメト￰ンメフ ￰ンメテ￰ンメモ￰ンメミ￰ンメリ￰ンメマ ￰ンメヌ￰ンメミ￰ンメル ￰ンメヒ￰ンメヨ￰ンメホ￰ンメム￰ンメヤ ￰ンメミ￰ンメラ￰ンメニ￰ンメモ ￰ンメユ￰ンメノ￰ンメニ ￰ンメヘ￰ンメツ￰ンメロ￰ンメレ ￰ンメナ￰ンメミ￰ンメネ",
"￰ンモᆪ￰ンモᄆ￰ンモᆴ ￰ンモᄎ￰ンモᄒ￰ンモᄇ￰ンモᆲ￰ンモᄡ ￰ンモᆱ￰ンモᄏ￰ンモᄌ￰ンヤタ￰ンモᄋ ￰ンモᆵ￰ンモᄌ￰ンヤチ ￰ンモᄈ￰ンモᄒ￰ンモᄊ￰ンモᄍ￰ンモᄐ ￰ンモᄌ￰ンモ﾿￰ンモᆴ￰ンモᄏ ￰ンモᄑ￰ンモᄆ￰ンモᆴ ￰ンモᄉ￰ンモᆰ￰ンヤテ￰ンヤツ ￰ンモᆳ￰ンモᄌ￰ンモᄚ",
"￰ンユヒ￰ンユル￰ンユヨ ￰ンユᄁ￰ンユᆭ￰ンユレ￰ンユヤ￰ンユワ ￰ンユモ￰ンユᆪ￰ンユᅠ￰ンユᄄ￰ンユ゚ ￰ンユラ￰ンユᅠ￰ンユᄅ ￰ンユロ￰ンユᆭ￰ンユ゙￰ンユᄀ￰ンユᄂ ￰ンユᅠ￰ンユᄃ￰ンユヨ￰ンユᆪ ￰ンユᆬ￰ンユル￰ンユヨ ￰ンユン￰ンユメ￰ンユᆱ￰ンユᆰ ￰ンユユ￰ンユᅠ￰ンユリ",
"￰ンレテ￰ンレム￰ンレホ ￰ンレレ￰ンレ゙￰ンレメ￰ンレフ￰ンレヤ ￰ンレヒ￰ンレロ￰ンレリ￰ンレᅠ￰ンレラ ￰ンレマ￰ンレリ￰ンレᄀ ￰ンレモ￰ンレ゙￰ンレヨ￰ンレル￰ンレワ ￰ンレリ￰ンレ゚￰ンレホ￰ンレロ ￰ンレン￰ンレム￰ンレホ ￰ンレユ￰ンレハ￰ンレᆪ￰ンレᄁ ￰ンレヘ￰ンレリ￰ンレミ",
"¬メᆵ¬メᆪ¬メᅠ ¬メᆲ¬メᄚ¬メᄂ¬メ゙¬メᆭ ¬メン¬メᆳ¬メᆰ¬メᄇ¬メᄅ ¬メᄀ¬メᆰ¬メᄈ ¬メᆬ¬メᄚ¬メᄄ¬メᆱ¬メᆴ ¬メᆰ¬メᄆ¬メᅠ¬メᆳ ¬メᆵ¬メᆪ¬メᅠ ¬メᄃ¬メワ¬メᄉ¬メᄡ ¬メ゚¬メᆰ¬メᄁ",
"<script>alert(123)</script>",
"&lt;script&gt;alert(&#39;123&#39;);&lt;/script&gt;",
"<img src=x onerror=alert(123) />",
"<svg><script>123<1>alert(123)</script>",
"\"><script>alert(123)</script>",
"'><script>alert(123)</script>",
"><script>alert(123)</script>",
"</script><script>alert(123)</script>",
"< / script >< script >alert(123)< / script >",
" onfocus=JaVaSCript:alert(123) autofocus",
"\" onfocus=JaVaSCript:alert(123) autofocus",
"' onfocus=JaVaSCript:alert(123) autofocus",
"￯ᄐワscript￯ᄐ゙alert(123)￯ᄐワ/script￯ᄐ゙",
"<sc<script>ript>alert(123)</sc</script>ript>",
"--><script>alert(123)</script>",
"\";alert(123);t=\"",
"';alert(123);t='",
"JavaSCript:alert(123)",
";alert(123);",
"src=JaVaSCript:prompt(132)",
"\"><script>alert(123);</script x=\"",
"'><script>alert(123);</script x='",
"><script>alert(123);</script x=",
"\" autofocus onkeyup=\"javascript:alert(123)",
"' autofocus onkeyup='javascript:alert(123)",
"<script\\x20type=\"text/javascript\">javascript:alert(1);</script>",
"<script\\x3Etype=\"text/javascript\">javascript:alert(1);</script>",
"<script\\x0Dtype=\"text/javascript\">javascript:alert(1);</script>",
"<script\\x09type=\"text/javascript\">javascript:alert(1);</script>",
"<script\\x0Ctype=\"text/javascript\">javascript:alert(1);</script>",
"<script\\x2Ftype=\"text/javascript\">javascript:alert(1);</script>",
"<script\\x0Atype=\"text/javascript\">javascript:alert(1);</script>",
"'`\"><\\x3Cscript>javascript:alert(1)</script>",
"'`\"><\\x00script>javascript:alert(1)</script>",
"ABC<div style=\"x\\x3Aexpression(javascript:alert(1)\">DEF",
"ABC<div style=\"x:expression\\x5C(javascript:alert(1)\">DEF",
"ABC<div style=\"x:expression\\x00(javascript:alert(1)\">DEF",
"ABC<div style=\"x:exp\\x00ression(javascript:alert(1)\">DEF",
"ABC<div style=\"x:exp\\x5Cression(javascript:alert(1)\">DEF",
"ABC<div style=\"x:\\x0Aexpression(javascript:alert(1)\">DEF",
"ABC<div style=\"x:\\x09expression(javascript:alert(1)\">DEF",
"ABC<div style=\"x:\\xE3\\x80\\x80expression(javascript:alert(1)\">DEF",
"ABC<div style=\"x:\\xE2\\x80\\x84expression(javascript:alert(1)\">DEF",
"ABC<div style=\"x:\\xC2\\xA0expression(javascript:alert(1)\">DEF",
"ABC<div style=\"x:\\xE2\\x80\\x80expression(javascript:alert(1)\">DEF",
"ABC<div style=\"x:\\xE2\\x80\\x8Aexpression(javascript:alert(1)\">DEF",
"ABC<div style=\"x:\\x0Dexpression(javascript:alert(1)\">DEF",
"ABC<div style=\"x:\\x0Cexpression(javascript:alert(1)\">DEF",
"ABC<div style=\"x:\\xE2\\x80\\x87expression(javascript:alert(1)\">DEF",
"ABC<div style=\"x:\\xEF\\xBB\\xBFexpression(javascript:alert(1)\">DEF",
"ABC<div style=\"x:\\x20expression(javascript:alert(1)\">DEF",
"ABC<div style=\"x:\\xE2\\x80\\x88expression(javascript:alert(1)\">DEF",
"ABC<div style=\"x:\\x00expression(javascript:alert(1)\">DEF",
"ABC<div style=\"x:\\xE2\\x80\\x8Bexpression(javascript:alert(1)\">DEF",
"ABC<div style=\"x:\\xE2\\x80\\x86expression(javascript:alert(1)\">DEF",
"ABC<div style=\"x:\\xE2\\x80\\x85expression(javascript:alert(1)\">DEF",
"ABC<div style=\"x:\\xE2\\x80\\x82expression(javascript:alert(1)\">DEF",
"ABC<div style=\"x:\\x0Bexpression(javascript:alert(1)\">DEF",
"ABC<div style=\"x:\\xE2\\x80\\x81expression(javascript:alert(1)\">DEF",
"ABC<div style=\"x:\\xE2\\x80\\x83expression(javascript:alert(1)\">DEF",
"ABC<div style=\"x:\\xE2\\x80\\x89expression(javascript:alert(1)\">DEF",
"<a href=\"\\x0Bjavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x0Fjavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\xC2\\xA0javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x05javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\xE1\\xA0\\x8Ejavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x18javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x11javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\xE2\\x80\\x88javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\xE2\\x80\\x89javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\xE2\\x80\\x80javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x17javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x03javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x0Ejavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x1Ajavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x00javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x10javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\xE2\\x80\\x82javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x20javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x13javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x09javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\xE2\\x80\\x8Ajavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x14javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x19javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\xE2\\x80\\xAFjavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x1Fjavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\xE2\\x80\\x81javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x1Djavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\xE2\\x80\\x87javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x07javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\xE1\\x9A\\x80javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\xE2\\x80\\x83javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x04javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x01javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x08javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\xE2\\x80\\x84javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\xE2\\x80\\x86javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\xE3\\x80\\x80javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x12javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x0Djavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x0Ajavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x0Cjavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x15javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\xE2\\x80\\xA8javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x16javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x02javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x1Bjavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x06javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\xE2\\x80\\xA9javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\xE2\\x80\\x85javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x1Ejavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\xE2\\x81\\x9Fjavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"\\x1Cjavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"javascript\\x00:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"javascript\\x3A:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"javascript\\x09:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"javascript\\x0D:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"<a href=\"javascript\\x0A:javascript:alert(1)\" id=\"fuzzelement1\">test</a>",
"`\"'><img src=xxx:x \\x0Aonerror=javascript:alert(1)>",
"`\"'><img src=xxx:x \\x22onerror=javascript:alert(1)>",
"`\"'><img src=xxx:x \\x0Bonerror=javascript:alert(1)>",
"`\"'><img src=xxx:x \\x0Donerror=javascript:alert(1)>",
"`\"'><img src=xxx:x \\x2Fonerror=javascript:alert(1)>",
"`\"'><img src=xxx:x \\x09onerror=javascript:alert(1)>",
"`\"'><img src=xxx:x \\x0Conerror=javascript:alert(1)>",
"`\"'><img src=xxx:x \\x00onerror=javascript:alert(1)>",
"`\"'><img src=xxx:x \\x27onerror=javascript:alert(1)>",
"`\"'><img src=xxx:x \\x20onerror=javascript:alert(1)>",
"\"`'><script>\\x3Bjavascript:alert(1)</script>",
"\"`'><script>\\x0Djavascript:alert(1)</script>",
"\"`'><script>\\xEF\\xBB\\xBFjavascript:alert(1)</script>",
"\"`'><script>\\xE2\\x80\\x81javascript:alert(1)</script>",
"\"`'><script>\\xE2\\x80\\x84javascript:alert(1)</script>",
"\"`'><script>\\xE3\\x80\\x80javascript:alert(1)</script>",
"\"`'><script>\\x09javascript:alert(1)</script>",
"\"`'><script>\\xE2\\x80\\x89javascript:alert(1)</script>",
"\"`'><script>\\xE2\\x80\\x85javascript:alert(1)</script>",
"\"`'><script>\\xE2\\x80\\x88javascript:alert(1)</script>",
"\"`'><script>\\x00javascript:alert(1)</script>",
"\"`'><script>\\xE2\\x80\\xA8javascript:alert(1)</script>",
"\"`'><script>\\xE2\\x80\\x8Ajavascript:alert(1)</script>",
"\"`'><script>\\xE1\\x9A\\x80javascript:alert(1)</script>",
"\"`'><script>\\x0Cjavascript:alert(1)</script>",
"\"`'><script>\\x2Bjavascript:alert(1)</script>",
"\"`'><script>\\xF0\\x90\\x96\\x9Ajavascript:alert(1)</script>",
"\"`'><script>-javascript:alert(1)</script>",
"\"`'><script>\\x0Ajavascript:alert(1)</script>",
"\"`'><script>\\xE2\\x80\\xAFjavascript:alert(1)</script>",
"\"`'><script>\\x7Ejavascript:alert(1)</script>",
"\"`'><script>\\xE2\\x80\\x87javascript:alert(1)</script>",
"\"`'><script>\\xE2\\x81\\x9Fjavascript:alert(1)</script>",
"\"`'><script>\\xE2\\x80\\xA9javascript:alert(1)</script>",
"\"`'><script>\\xC2\\x85javascript:alert(1)</script>",
"\"`'><script>\\xEF\\xBF\\xAEjavascript:alert(1)</script>",
"\"`'><script>\\xE2\\x80\\x83javascript:alert(1)</script>",
"\"`'><script>\\xE2\\x80\\x8Bjavascript:alert(1)</script>",
"\"`'><script>\\xEF\\xBF\\xBEjavascript:alert(1)</script>",
"\"`'><script>\\xE2\\x80\\x80javascript:alert(1)</script>",
"\"`'><script>\\x21javascript:alert(1)</script>",
"\"`'><script>\\xE2\\x80\\x82javascript:alert(1)</script>",
"\"`'><script>\\xE2\\x80\\x86javascript:alert(1)</script>",
"\"`'><script>\\xE1\\xA0\\x8Ejavascript:alert(1)</script>",
"\"`'><script>\\x0Bjavascript:alert(1)</script>",
"\"`'><script>\\x20javascript:alert(1)</script>",
"\"`'><script>\\xC2\\xA0javascript:alert(1)</script>",
"<img \\x00src=x onerror=\"alert(1)\">",
"<img \\x47src=x onerror=\"javascript:alert(1)\">",
"<img \\x11src=x onerror=\"javascript:alert(1)\">",
"<img \\x12src=x onerror=\"javascript:alert(1)\">",
"<img\\x47src=x onerror=\"javascript:alert(1)\">",
"<img\\x10src=x onerror=\"javascript:alert(1)\">",
"<img\\x13src=x onerror=\"javascript:alert(1)\">",
"<img\\x32src=x onerror=\"javascript:alert(1)\">",
"<img\\x47src=x onerror=\"javascript:alert(1)\">",
"<img\\x11src=x onerror=\"javascript:alert(1)\">",
"<img \\x47src=x onerror=\"javascript:alert(1)\">",
"<img \\x34src=x onerror=\"javascript:alert(1)\">",
"<img \\x39src=x onerror=\"javascript:alert(1)\">",
"<img \\x00src=x onerror=\"javascript:alert(1)\">",
"<img src\\x09=x onerror=\"javascript:alert(1)\">",
"<img src\\x10=x onerror=\"javascript:alert(1)\">",
"<img src\\x13=x onerror=\"javascript:alert(1)\">",
"<img src\\x32=x onerror=\"javascript:alert(1)\">",
"<img src\\x12=x onerror=\"javascript:alert(1)\">",
"<img src\\x11=x onerror=\"javascript:alert(1)\">",
"<img src\\x00=x onerror=\"javascript:alert(1)\">",
"<img src\\x47=x onerror=\"javascript:alert(1)\">",
"<img src=x\\x09onerror=\"javascript:alert(1)\">",
"<img src=x\\x10onerror=\"javascript:alert(1)\">",
"<img src=x\\x11onerror=\"javascript:alert(1)\">",
"<img src=x\\x12onerror=\"javascript:alert(1)\">",
"<img src=x\\x13onerror=\"javascript:alert(1)\">",
"<img[a][b][c]src[d]=x[e]onerror=[f]\"alert(1)\">",
"<img src=x onerror=\\x09\"javascript:alert(1)\">",
"<img src=x onerror=\\x10\"javascript:alert(1)\">",
"<img src=x onerror=\\x11\"javascript:alert(1)\">",
"<img src=x onerror=\\x12\"javascript:alert(1)\">",
"<img src=x onerror=\\x32\"javascript:alert(1)\">",
"<img src=x onerror=\\x00\"javascript:alert(1)\">",
"<a href=java&#1&#2&#3&#4&#5&#6&#7&#8&#11&#12script:javascript:alert(1)>XXX</a>",
"<img src=\"x` `<script>javascript:alert(1)</script>\"` `>",
"<img src onerror /\" '\"= alt=javascript:alert(1)//\">",
"<title onpropertychange=javascript:alert(1)></title><title title=>",
"<a href=http://foo.bar/#x=`y></a><img alt=\"`><img src=x:x onerror=javascript:alert(1)></a>\">",
"<!--[if]><script>javascript:alert(1)</script -->",
"<!--[if<img src=x onerror=javascript:alert(1)//]> -->",
"<script src=\"/\\%(jscript)s\"></script>",
"<script src=\"\\\\%(jscript)s\"></script>",
"<IMG \"\"\"><SCRIPT>alert(\"XSS\")</SCRIPT>\">",
"<IMG SRC=javascript:alert(String.fromCharCode(88,83,83))>",
"<IMG SRC=# onmouseover=\"alert('xxs')\">",
"<IMG SRC= onmouseover=\"alert('xxs')\">",
"<IMG onmouseover=\"alert('xxs')\">",
"<IMG SRC=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;>",
"<IMG SRC=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>",
"<IMG SRC=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>",
"<IMG SRC=\"jav ascript:alert('XSS');\">",
"<IMG SRC=\"jav&#x09;ascript:alert('XSS');\">",
"<IMG SRC=\"jav&#x0A;ascript:alert('XSS');\">",
"<IMG SRC=\"jav&#x0D;ascript:alert('XSS');\">",
"perl -e 'print \"<IMG SRC=java\\0script:alert(\\\"XSS\\\")>\";' > out",
"<IMG SRC=\" &#14; javascript:alert('XSS');\">",
"<SCRIPT/XSS SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT>",
"<BODY onload!#$%&()*~+-_.,:;?@[/|\\]^`=alert(\"XSS\")>",
"<SCRIPT/SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT>",
"<<SCRIPT>alert(\"XSS\");//<</SCRIPT>",
"<SCRIPT SRC=http://ha.ckers.org/xss.js?< B >",
"<SCRIPT SRC=//ha.ckers.org/.j>",
"<IMG SRC=\"javascript:alert('XSS')\"",
"<iframe src=http://ha.ckers.org/scriptlet.html <",
"\\\";alert('XSS');//",
"<u oncopy=alert()> Copy me</u>",
"<i onwheel=alert(1)> Scroll over me </i>",
"<plaintext>",
"http://a/%%30%30",
"</textarea><script>alert(123)</script>",
"1;DROP TABLE users",
"1'; DROP TABLE users-- 1",
"' OR 1=1 -- 1",
"' OR '1'='1",
"'; EXEC sp_MSForEachTable 'DROP TABLE ?'; --",
" ",
"%",
"_",
"-",
"--",
"--version",
"--help",
"$USER",
"/dev/null; touch /tmp/blns.fail ; echo",
"`touch /tmp/blns.fail`",
"$(touch /tmp/blns.fail)",
"@{[system \"touch /tmp/blns.fail\"]}",
"eval(\"puts 'hello world'\")",
"System(\"ls -al /\")",
"`ls -al /`",
"Kernel.exec(\"ls -al /\")",
"Kernel.exit(1)",
"%x('ls -al /')",
"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><!DOCTYPE foo [ <!ELEMENT foo ANY ><!ENTITY xxe SYSTEM \"file:///etc/passwd\" >]><foo>&xxe;</foo>",
"$HOME",
"$ENV{'HOME'}",
"%d",
"%s%s%s%s%s",
"{0}",
"%*.*s",
"%@",
"%n",
"File:///",
"../../../../../../../../../../../etc/passwd%00",
"../../../../../../../../../../../etc/hosts",
"() { 0; }; touch /tmp/blns.shellshock1.fail;",
"() { _; } >_[$($())] { touch /tmp/blns.shellshock2.fail; }",
"<<< %s(un='%s') = %u",
"+++ATH0",
"CON",
"PRN",
"AUX",
"CLOCK$",
"NUL",
"A:",
"ZZ:",
"COM1",
"LPT1",
"LPT2",
"LPT3",
"COM2",
"COM3",
"COM4",
"DCC SEND STARTKEYLOGGER 0 0 0",
"Scunthorpe General Hospital",
"Penistone Community Church",
"Lightwater Country Park",
"Jimmy Clitheroe",
"Horniman Museum",
"shitake mushrooms",
"RomansInSussex.co.uk",
"http://www.cum.qc.ca/",
"Craig Cockburn, Software Specialist",
"Linda Callahan",
"Dr. Herman I. Libshitz",
"magna cum laude",
"Super Bowl XXX",
"medieval erection of parapets",
"evaluate",
"mocha",
"expression",
"Arsenal canal",
"classic",
"Tyson Gay",
"Dick Van Dyke",
"basement",
"If you're reading this, you've been in a coma for almost 20 years now. We're trying a new technique. We don't know where this message will end up in your dream, but we hope it works. Please wake up, we miss you.",
"Roses are \u001b[0;31mred\u001b[0m, violets are \u001b[0;34mblue. Hope you enjoy terminal hue",
"But now...\u001b[20Cfor my greatest trick...\u001b[8m",
"The quic\b\b\b\b\b\bk brown fo\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007x... [Beeeep]",
"Power￙ト￙マ￙ト￙マ￘ᄉ￙ム￘ᄄ￙マ￙ト￙マ￙ト￘ᄉ￙ム￘ᄄ￙マ￘ᄆ￘ᄆ￙ヒ ¢ᆬᆪ ¢ᆬᆪh ¢ᆬᆪ ¢ᆬᆪ¥ニラ",
"゚マᄈ0゚フネ￯ᄌマ",
"¢ᄚワ¢ᄆヘ¢ᄚ゙¬タフ¢ᄚᄒ",
"ᅳᆵᅳニ￙흐リ",
"{% print 'x' * 64 * 1024**3 %}",
"{{ \"\".__class__.__mro__[2].__subclasses__()[40](\"/etc/passwd\").read() }}"
]

View File

@ -1,260 +0,0 @@
/* eslint-env jest */
'use strict'
const Hexo = require('hexo')
const zlib = require('node:zlib')
const { promisify } = require('node:util')
const gzip = promisify(zlib.gzip)
const unzip = promisify(zlib.unzip)
describe('gzip', () => {
const hexo = new Hexo(__dirname)
const g = require('../lib/filter').gzipFn.bind(hexo)
const path = 'foo.txt'
const input = 'Lorem ipsum dolor sit amet consectetur adipiscing elit fusce'
beforeEach(() => {
hexo.config.minify = {
gzip: {
enable: true,
verbose: false,
include: ['*.html', '*.css', '*.js', '*.txt', '*.ttf', '*.atom', '*.stl', '*.xml', '*.svg', '*.eot', '*.json'],
globOptions: { basename: true }
}
}
hexo.route.set(path, input)
})
afterEach(() => {
const routeList = hexo.route.list()
routeList.forEach((path) => hexo.route.remove(path))
})
test('default', async () => {
await g()
const output = hexo.route.get(path.concat('.gz'))
const buf = []
output.on('data', (chunk) => (buf.push(chunk)))
output.on('end', async () => {
const result = Buffer.concat(buf)
const expected = await gzip(input, { level: zlib.constants.Z_BEST_COMPRESSION })
const resultUnzip = await unzip(result)
const expectedUnzip = await unzip(expected)
expect(result.equals(expected)).toBe(true)
expect(resultUnzip.toString()).toBe(input)
expect(expectedUnzip.toString()).toBe(input)
})
})
test('disable', async () => {
hexo.config.minify.gzip.enable = false
const result = await g()
expect(result).toBeUndefined()
})
test('empty file', async () => {
hexo.route.set(path, '')
const routeList = hexo.route.list()
expect(routeList).not.toContain(path.concat('.gz'))
const result = await g()
// empty file resolves to an array of undefined
expect(result).toBeDefined()
expect(result[0]).toBeUndefined()
})
test('option', async () => {
const customOpt = {
level: 1
}
hexo.config.minify.gzip.level = customOpt.level
await g()
const output = hexo.route.get(path.concat('.gz'))
const buf = []
output.on('data', (chunk) => (buf.push(chunk)))
output.on('end', async () => {
const result = Buffer.concat(buf)
const expected = await gzip(input, customOpt)
expect(result.equals(expected)).toBe(true)
})
})
test('option - verbose', async () => {
hexo.config.minify.gzip.verbose = true
hexo.log.log = jest.fn()
await g()
expect(hexo.log.log.mock.calls[0][0]).toContain(`gzip: ${path}`)
})
test('option - invalid', async () => {
const customOpt = {
level: 9000
}
hexo.config.minify.gzip.level = customOpt.level
let expected
try {
await gzip(input, customOpt)
} catch (err) {
expected = err
}
expect(expected).toBeDefined()
await expect(g()).rejects.toThrow(`Path: ${path}\n${expected}`)
})
test('include - exclude non-text file by default', async () => {
const path = 'foo.jpg'
hexo.route.set(path, input)
await g()
const result = hexo.route.get(path.concat('.gz'))
expect(result).toBeUndefined()
})
test('include - basename', async () => {
hexo.config.minify.gzip.include = 'bar.txt'
const path = 'foo/bar.txt'
hexo.route.set(path, input)
await g()
const result = hexo.route.get(path.concat('.gz'))
expect(result).toBeDefined()
})
test('include - slash in pattern', async () => {
hexo.config.minify.gzip.include = '**/lectus/**/*.txt'
const path = 'eleifend/lectus/nullam/dapibus/netus.txt'
hexo.route.set(path, input)
await g()
const result = hexo.route.get(path.concat('.gz'))
expect(result).toBeDefined()
})
test('include - basename + slash + basename enabled', async () => {
hexo.route.remove(path)
const paths = [
'lorem/ipsum/dolor.html',
'gravida/sociis/erat/ante.css',
'aptent/elementum.js',
'felis/blandit/cursus.svg'
]
hexo.config.minify.gzip.include = [
'*.html',
'**/sociis/**/*.css'
]
paths.forEach((inpath) => {
hexo.route.set(inpath, input)
})
await g()
const routeList = hexo.route.list()
const expected = [
'lorem/ipsum/dolor.html.gz',
'gravida/sociis/erat/ante.css.gz'
]
const notExpected = [
'aptent/elementum.js.gz',
'felis/blandit/cursus.svg.gz'
]
expect(routeList).toEqual(expect.arrayContaining(expected))
expect(routeList).toEqual(expect.not.arrayContaining(notExpected))
})
test('include - basename + slash + basename disabled', async () => {
hexo.route.remove(path)
const paths = [
'lorem/ipsum/dolor.html',
'gravida/sociis/erat/ante.css',
'aptent/elementum.js',
'felis/blandit/cursus.svg'
]
hexo.config.minify.gzip.include = [
'*.html',
'**/sociis/**/*.css'
]
hexo.config.minify.gzip.globOptions = {
basename: false
}
paths.forEach((inpath) => {
hexo.route.set(inpath, input)
})
await g()
const routeList = hexo.route.list()
const expected = [
'gravida/sociis/erat/ante.css.gz'
]
const notExpected = [
'lorem/ipsum/dolor.html.gz',
'aptent/elementum.js.gz',
'felis/blandit/cursus.svg.gz'
]
expect(routeList).toEqual(expect.arrayContaining(expected))
expect(routeList).toEqual(expect.not.arrayContaining(notExpected))
})
test('include - reverse pattern + basename disabled', async () => {
hexo.route.remove(path)
const paths = [
'lorem/ipsum/dolor.html',
'gravida/sociis/erat/ante.css',
'aptent/elementum.js',
'felis/blandit/cursus.svg'
]
hexo.config.minify.gzip.include = [
'!dolor.html'
]
hexo.config.minify.gzip.globOptions = {
basename: false
}
paths.forEach((inpath) => {
hexo.route.set(inpath, input)
})
await g()
const routeList = hexo.route.list()
const expected = paths.map((path) => path.concat('.gz'))
expect(routeList).toEqual(expect.arrayContaining(expected))
})
test('blns', async () => {
const blns = require('./fixtures/blns.json')
for (const nStr of blns) {
hexo.route.remove(path)
hexo.route.set(path, nStr)
await g()
const output = hexo.route.get(path.concat('.gz'))
const buf = []
output.on('data', (chunk) => (buf.push(chunk)))
output.on('end', async () => {
const result = Buffer.concat(buf)
const resultUnzip = await unzip(result)
expect(resultUnzip.toString()).toBe(nStr)
})
}
})
})

View File

@ -1,132 +0,0 @@
/* eslint-env jest */
'use strict'
const Hexo = require('hexo')
const { minify: htmlMinify } = require('html-minifier-terser')
describe('html', () => {
const hexo = new Hexo(__dirname)
const h = require('../lib/filter').minifyHtml.bind(hexo)
const input = '<p id="">foo</p>'
const path = 'index.html'
const defaultCfg = {
html: {
enable: true,
verbose: false,
exclude: [],
collapseBooleanAttributes: true,
collapseWhitespace: true,
ignoreCustomComments: [/^\s*more/],
removeComments: true,
removeEmptyAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
minifyJS: true,
minifyCSS: true,
globOptions: { basename: true }
}
}
let expected = ''
beforeAll(async () => {
expected = await htmlMinify(input, defaultCfg.html)
})
beforeEach(() => {
hexo.config.minify = JSON.parse(JSON.stringify(defaultCfg))
})
test('default', async () => {
const result = await h(input, { path })
expect(result).toBe(expected)
})
test('disable', async () => {
hexo.config.minify.html.enable = false
const result = await h(input, { path })
expect(result).toBeUndefined()
})
test('empty file', async () => {
const result = await h('', { path })
expect(result).toBeUndefined()
})
test('option', async () => {
const customOpt = { removeEmptyAttributes: false }
hexo.config.minify.html = customOpt
const result = await h(input, { path })
const expected = await htmlMinify(input, customOpt)
expect(result).toBe(input)
expect(result).toBe(expected)
})
test('option - verbose', async () => {
hexo.config.minify.html.verbose = true
hexo.log.log = jest.fn()
await h(input, { path })
expect(hexo.log.log.mock.calls[0][0]).toContain(`html: ${path}`)
})
test('exclude', async () => {
const exclude = '*.min.html'
hexo.config.minify.html.exclude = exclude
const result = await h(input, { path: 'foo/bar.min.html' })
expect(result).toBe(input)
})
test('exclude - slash in pattern', async () => {
const exclude = '**/lectus/**/*.html'
hexo.config.minify.html.exclude = exclude
const result = await h(input, { path: 'eleifend/lectus/nullam/dapibus/netus.html' })
expect(result).toBe(input)
})
test('exclude - basename is true + slash', async () => {
const exclude = ['**/lectus/**/*.html', 'bar.html']
const globOptions = { basename: true }
hexo.config.minify.html.exclude = exclude
hexo.config.minify.html.globOptions = globOptions
const result = await h(input, { path: 'foo/bar.html' })
expect(result).toBe(input)
})
test('exclude - basename is false + slash', async () => {
const exclude = ['**/lectus/**/*.html', 'bar.html']
const globOptions = { basename: false }
hexo.config.minify.html.exclude = exclude
hexo.config.minify.html.globOptions = globOptions
const result = await h(input, { path: 'foo/bar.html' })
expect(result).toBe(expected)
})
test('null', async () => {
hexo.config.minify.html.exclude = null
hexo.config.minify.html.globOptions = null
const result = await h(input, { path: null })
expect(result).toBe(expected)
})
test('invalid string', async () => {
const invalid = '<html><>?:"{}|_+</html>'
await expect(h(invalid, { path })).rejects.toThrow('Parse Error: <>?:"{}|_+</html>')
})
})

View File

@ -1,123 +0,0 @@
/* eslint-env jest */
'use strict'
const Hexo = require('hexo')
const { minify: terserMinify } = require('terser')
describe('js', () => {
const hexo = new Hexo(__dirname)
const j = require('../lib/filter').minifyJs.bind(hexo)
const input = 'var o = { "foo": 1, bar: 3 };'
const path = 'foo.js'
let expected = ''
beforeAll(async () => {
const { code } = await terserMinify(input, { mangle: true })
expected = code
})
beforeEach(async () => {
hexo.config.minify = {
js: {
enable: true,
verbose: false,
exclude: ['*.min.js'],
compress: {},
mangle: true,
output: {},
globOptions: { basename: true }
}
}
})
test('default', async () => {
const result = await j(input, { path })
expect(result).toBeDefined()
expect(expected).toBeDefined()
expect(result).toBe(expected)
})
test('disable', async () => {
hexo.config.minify.js.enable = false
const result = await j(input, { path })
expect(result).toBeUndefined()
})
test('empty file', async () => {
const result = await j('', { path })
expect(result).toBeUndefined()
})
test('option', async () => {
const customOpt = {
mangle: {
properties: true
}
}
hexo.config.minify.js = customOpt
const result = await j(input, { path })
const { code: expected } = await terserMinify(input, customOpt)
expect(result).toBe(expected)
})
test('option - verbose', async () => {
hexo.config.minify.js.verbose = true
hexo.log.log = jest.fn()
await j(input, { path })
expect(hexo.log.log.mock.calls[0][0]).toContain(`js: ${path}`)
})
test('option - invalid', async () => {
const customOpt = {
mangle: {
foo: 'bar'
}
}
hexo.config.minify.js = customOpt
let expected
try {
await terserMinify(input, customOpt)
} catch (err) {
expected = err
}
expect(expected).toBeDefined()
await expect(j(input, { path })).rejects.toThrow(`Path: ${path}\n${expected}`)
})
test('exclude - *.min.js', async () => {
const result = await j(input, { path: 'foo/bar.min.js' })
expect(result).toBe(input)
})
test('exclude - basename', async () => {
const exclude = '*baz.js'
hexo.config.minify.js.exclude = exclude
const result = await j(input, { path: 'foo/barbaz.js' })
expect(result).toBe(input)
})
test('exclude - slash in pattern', async () => {
const exclude = '**/lectus/**/*.js'
hexo.config.minify.js.exclude = exclude
const result = await j(input, { path: 'eleifend/lectus/nullam/dapibus/netus.js' })
expect(result).toBe(input)
})
test('invalid string', async () => {
const invalid = 'console.log("\\");'
await expect(j(invalid, { path })).rejects.toThrow(`Path: ${path}\nSyntaxError`)
})
})

View File

@ -1,190 +0,0 @@
/* eslint-env jest */
'use strict'
const Hexo = require('hexo')
describe('xml', () => {
const hexo = new Hexo(__dirname)
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"]}'
beforeEach(() => {
hexo.config.minify = {
json: {
enable: false,
verbose: false,
include: ['*.json', '!*.min.json'],
globOptions: { basename: true }
}
}
// 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')
await expect(jsonFn()).rejects.toThrow(`Path: ${path}\nSyntaxError`)
})
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)
})
})

View File

@ -1,253 +0,0 @@
/* eslint-env jest */
'use strict'
const Hexo = require('hexo')
const { optimize: svgOptimize } = require('svgo')
describe('svg', () => {
const hexo = new Hexo(__dirname)
const s = require('../lib/filter').minifySvg.bind(hexo)
const input = '<svg><rect x="1" y="2" width="3" height="4" id="a"/></svg>'
const path = 'foo.svg'
// svgo's plugins option
let plugins = [{
name: 'preset-default',
params: {
overrides: {}
}
}]
beforeEach(() => {
hexo.config.minify = {
svg: {
enable: true,
verbose: false,
include: ['*.svg', '!*.min.svg'],
plugins: {},
globOptions: { basename: true }
}
}
plugins = [{
name: 'preset-default',
params: {
overrides: {}
}
}]
hexo.route.set(path, input)
})
afterEach(() => {
const routeList = hexo.route.list()
routeList.forEach((path) => hexo.route.remove(path))
})
test('default', async () => {
await s()
const { data } = svgOptimize(input, { plugins })
const output = hexo.route.get(path)
let result = ''
output.on('data', (chunk) => (result += chunk))
output.on('end', () => {
expect(result).toBe(data)
})
})
test('disable', async () => {
hexo.config.minify.svg.enable = false
const result = await s()
expect(result).toBeUndefined()
})
test('empty file', async () => {
hexo.route.set(path, '')
const result = await s()
// empty file resolves to an array of undefined
expect(result).toBeDefined()
expect(result[0]).toBeUndefined()
})
test('option', async () => {
const customOpt = {
cleanupIds: false
}
hexo.config.minify.svg.plugins = customOpt
plugins = [{
name: 'preset-default',
params: {
overrides: customOpt
}
}]
await s()
const { data } = svgOptimize(input, { plugins })
const output = hexo.route.get(path)
let result = ''
output.on('data', (chunk) => (result += chunk))
output.on('end', () => {
expect(result).toBe(data)
expect(result).toContain('id="a"')
})
})
test('option - verbose', async () => {
hexo.config.minify.svg.verbose = true
hexo.log.log = jest.fn()
await s()
expect(hexo.log.log.mock.calls[0][0]).toContain(`svg: ${path}`)
})
test('option - plugins - invalid', async () => {
hexo.config.minify.svg.plugins = 'invalid'
await s()
const { data } = svgOptimize(input, { plugins })
const output = hexo.route.get(path)
let result = ''
output.on('data', (chunk) => (result += chunk))
output.on('end', () => {
expect(result).toBe(data)
})
})
test('invalid svg', async () => {
const input = '{}'
hexo.route.set(path, input)
let expected
try {
svgOptimize(input, { plugins })
} catch (err) {
expected = err
}
expect(expected).toBeDefined()
await expect(s()).rejects.toThrow(`Path: ${path}\n${expected}`)
})
test('include - exclude *.min.svg by default', async () => {
const path = 'foo.min.svg'
hexo.route.set(path, input)
await s()
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.svg.include = 'bar.svg'
const path = 'foo/bar.svg'
hexo.route.set(path, input)
await s()
const { data } = svgOptimize(input, { plugins })
const output = hexo.route.get(path)
let result = ''
output.on('data', (chunk) => (result += chunk))
output.on('end', () => {
expect(result).toBe(data)
})
})
test('include - slash in pattern', async () => {
hexo.config.minify.svg.include = '**/lectus/**/*.svg'
const path = 'eleifend/lectus/nullam/dapibus/netus.svg'
hexo.route.set(path, input)
await s()
const { data } = svgOptimize(input, { plugins })
const output = hexo.route.get(path)
let result = ''
output.on('data', (chunk) => (result += chunk))
output.on('end', () => {
expect(result).toBe(data)
})
})
test('include - basename + slash', async () => {
hexo.route.remove(path)
const paths = [
'lorem/ipsum/dolor.svg',
'gravida/sociis/erat/ante.svg',
'aptent/elementum.svg',
'felis/blandit/cursus.svg'
]
hexo.config.minify.svg.include = [
'dolor.svg',
'**/sociis/**/*.svg'
]
paths.forEach((inpath) => {
hexo.route.set(inpath, input)
})
await s()
const { data } = svgOptimize(input, { plugins })
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(data)
})
})
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.svg',
'gravida/sociis/erat/ante.svg',
'aptent/elementum.svg',
'felis/blandit/cursus.svg'
]
hexo.config.minify.svg.include = [
'!dolor.svg'
]
hexo.config.minify.svg.globOptions = {
basename: false
}
paths.forEach((inpath) => {
hexo.route.set(inpath, input)
})
await s()
const { data } = svgOptimize(input, { plugins })
paths.forEach((inpath) => {
const output = hexo.route.get(inpath)
let result = ''
output.on('data', (chunk) => (result += chunk))
output.on('end', () => {
expect(result).toBe(data)
})
})
})
test('include - empty route', async () => {
hexo.route.remove(path)
const result = await s()
expect(result.length).toBe(0)
})
})

View File

@ -1,213 +0,0 @@
/* eslint-env jest */
'use strict'
const Hexo = require('hexo')
describe('xml', () => {
const hexo = new Hexo(__dirname)
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: {
enable: false,
verbose: false,
include: ['*.xml', '!*.min.xml'],
removeComments: true,
globOptions: { basename: true }
}
}
// 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)
})
test('avoid processing CDATA', async () => {
const input = '<foo><![CDATA[<p>lorem</p>\n<p>ipsum</p>]]></foo>'
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)
})
})
})

View File

@ -1,256 +0,0 @@
/* eslint-env jest */
'use strict'
const Hexo = require('hexo')
const { compress: zstd, decompress: unzstd } = require('@mongodb-js/zstd')
describe('zstd', () => {
const hexo = new Hexo(__dirname)
const z = require('../lib/filter').zstdFn.bind(hexo)
const path = 'foo.txt'
const input = 'Lorem ipsum dolor sit amet consectetur adipiscing elit fusce'
const inputBuf = Buffer.from(input, 'utf8')
beforeEach(() => {
hexo.config.minify = {
zstd: {
enable: true,
verbose: false,
include: ['*.html', '*.css', '*.js', '*.txt', '*.ttf', '*.atom', '*.stl', '*.xml', '*.svg', '*.eot', '*.json'],
globOptions: { basename: true }
}
}
hexo.route.set(path, input)
})
afterEach(() => {
const routeList = hexo.route.list()
routeList.forEach((path) => hexo.route.remove(path))
})
test('default', async () => {
await z()
const output = hexo.route.get(path.concat('.zst'))
const buf = []
output.on('data', (chunk) => (buf.push(chunk)))
output.on('end', async () => {
const result = Buffer.concat(buf)
const expected = await zstd(inputBuf)
const resultUnzst = await unzstd(result)
const expectedUnzst = await unzstd(expected)
expect(result.equals(expected)).toBe(true)
expect(resultUnzst.toString()).toBe(input)
expect(expectedUnzst.toString()).toBe(input)
})
})
test('disable', async () => {
hexo.config.minify.zstd.enable = false
const result = await z()
expect(result).toBeUndefined()
})
test('empty file', async () => {
hexo.route.set(path, '')
const routeList = hexo.route.list()
expect(routeList).not.toContain(path.concat('.zst'))
const result = await z()
expect(result).toBeDefined()
expect(result[0]).toBeUndefined()
})
test('option', async () => {
const level = 1
hexo.config.minify.zstd.level = level
await z()
const output = hexo.route.get(path.concat('.zst'))
const buf = []
output.on('data', (chunk) => (buf.push(chunk)))
output.on('end', async () => {
const result = Buffer.concat(buf)
const expected = await zstd(inputBuf, level)
expect(result.equals(expected)).toBe(true)
})
})
test('option - verbose', async () => {
hexo.config.minify.zstd.verbose = true
hexo.log.log = jest.fn()
await z()
expect(hexo.log.log.mock.calls[0][0]).toContain(`zstd: ${path}`)
})
test('option - level is string', async () => {
const level = 'foo'
hexo.config.minify.zstd.level = level
await z()
const output = hexo.route.get(path.concat('.zst'))
const buf = []
output.on('data', (chunk) => (buf.push(chunk)))
output.on('end', async () => {
const result = Buffer.concat(buf)
const expected = await zstd(inputBuf, undefined)
expect(result.equals(expected)).toBe(true)
})
})
test('include - exclude non-text file by default', async () => {
const path = 'foo.jpg'
hexo.route.set(path, input)
await z()
const result = hexo.route.get(path.concat('.zst'))
expect(result).toBeUndefined()
})
test('include - basename', async () => {
hexo.config.minify.zstd.include = 'bar.txt'
const path = 'foo/bar.txt'
hexo.route.set(path, input)
await z()
const result = hexo.route.get(path.concat('.zst'))
expect(result).toBeDefined()
})
test('include - slash in pattern', async () => {
hexo.config.minify.zstd.include = '**/lectus/**/*.txt'
const path = 'eleifend/lectus/nullam/dapibus/netus.txt'
hexo.route.set(path, input)
await z()
const result = hexo.route.get(path.concat('.zst'))
expect(result).toBeDefined()
})
test('include - basename + slash + basename enabled', async () => {
hexo.route.remove(path)
const paths = [
'lorem/ipsum/dolor.html',
'gravida/sociis/erat/ante.css',
'aptent/elementum.js',
'felis/blandit/cursus.svg'
]
hexo.config.minify.zstd.include = [
'*.html',
'**/sociis/**/*.css'
]
paths.forEach((inpath) => {
hexo.route.set(inpath, input)
})
await z()
const routeList = hexo.route.list()
const expected = [
'lorem/ipsum/dolor.html.zst',
'gravida/sociis/erat/ante.css.zst'
]
const notExpected = [
'aptent/elementum.js.zst',
'felis/blandit/cursus.svg.zst'
]
expect(routeList).toEqual(expect.arrayContaining(expected))
expect(routeList).toEqual(expect.not.arrayContaining(notExpected))
})
test('include - basename + slash + basename disabled', async () => {
hexo.route.remove(path)
const paths = [
'lorem/ipsum/dolor.html',
'gravida/sociis/erat/ante.css',
'aptent/elementum.js',
'felis/blandit/cursus.svg'
]
hexo.config.minify.zstd.include = [
'*.html',
'**/sociis/**/*.css'
]
hexo.config.minify.zstd.globOptions = {
basename: false
}
paths.forEach((inpath) => {
hexo.route.set(inpath, input)
})
await z()
const routeList = hexo.route.list()
const expected = [
'gravida/sociis/erat/ante.css.zst'
]
const notExpected = [
'lorem/ipsum/dolor.html.zst',
'aptent/elementum.js.zst',
'felis/blandit/cursus.svg.zst'
]
expect(routeList).toEqual(expect.arrayContaining(expected))
expect(routeList).toEqual(expect.not.arrayContaining(notExpected))
})
test('include - reverse pattern + basename disabled', async () => {
hexo.route.remove(path)
const paths = [
'lorem/ipsum/dolor.html',
'gravida/sociis/erat/ante.css',
'aptent/elementum.js',
'felis/blandit/cursus.svg'
]
hexo.config.minify.zstd.include = [
'!dolor.html'
]
hexo.config.minify.zstd.globOptions = {
basename: false
}
paths.forEach((inpath) => {
hexo.route.set(inpath, input)
})
await z()
const routeList = hexo.route.list()
const expected = paths.map((path) => path.concat('.zst'))
expect(routeList).toEqual(expect.arrayContaining(expected))
})
test('blns', async () => {
const blns = require('./fixtures/blns.json')
for (const nStr of blns) {
hexo.route.remove(path)
hexo.route.set(path, nStr)
await z()
const output = hexo.route.get(path.concat('.zst'))
const buf = []
output.on('data', (chunk) => (buf.push(chunk)))
output.on('end', async () => {
const result = Buffer.concat(buf)
const resultUnzst = await unzstd(result)
expect(resultUnzst.toString()).toBe(nStr)
})
}
})
})