mirror of https://github.com/curbengh/hexo-yam
Compare commits
75 Commits
Author | SHA1 | Date |
---|---|---|
|
4bd000bfee | |
|
5fb3349dec | |
|
80b2325cbf | |
|
812ff614fc | |
|
702eb2fa0c | |
|
a2c810cfb5 | |
|
30c33c0ad3 | |
|
1c9d91b524 | |
|
c18f4c8476 | |
|
e33fd0ed5d | |
|
73db847fd4 | |
|
7639547408 | |
|
f7df26377a | |
|
1b04f7c8f1 | |
|
18a5d8108b | |
|
7f2033e553 | |
|
e9584b2e0b | |
|
e99e110d39 | |
|
a5f19dbe79 | |
|
c3ed0c68c7 | |
|
f92017f688 | |
|
0abd90c78a | |
|
426da85620 | |
|
bfb886abac | |
|
d99caafe90 | |
|
3cc6f5e79e | |
|
56af48f632 | |
|
0bee9ada0e | |
|
1af8c52cdb | |
|
81ceea413d | |
|
8e937ead21 | |
|
7809098034 | |
|
dffeb0cfc7 | |
|
bfcd93edb7 | |
|
2992e8d8e7 | |
|
cf7e8f675c | |
|
3523b0fd98 | |
|
df3b0513e7 | |
|
dbed65ffeb | |
|
c964cfcf80 | |
|
793bcd81c4 | |
|
f65e7e7a22 | |
|
d9e6228960 | |
|
6c6060f28c | |
|
79f4f24f8e | |
|
b0575e6fdc | |
|
a6f434ff8d | |
|
a6e2ea0879 | |
|
9c8a228e09 | |
|
4b4d3cb342 | |
|
8258359f6b | |
|
c6c7d3743a | |
|
777ffabd2b | |
|
5453fc9ee0 | |
|
c3b899556a | |
|
866ba28c3c | |
|
b54324c154 | |
|
077055ed0b | |
|
d54ed360be | |
|
1ae4d77071 | |
|
0f73e6ed98 | |
|
79f15acb79 | |
|
2ae5520a60 | |
|
ef46c3c6d9 | |
|
4648fa32f1 | |
|
197836b82b | |
|
d757c1b5c7 | |
|
0623de5252 | |
|
fbb2c71e87 | |
|
b3d09fec7a | |
|
3edbe68008 | |
|
1833ffa76e | |
|
63eee78e01 | |
|
7e81770894 | |
|
288a4e3232 |
|
@ -6,19 +6,17 @@ jobs:
|
||||||
linter:
|
linter:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.3.4
|
- uses: actions/checkout@v4
|
||||||
- name: Use Node.js 14.x
|
- name: Install Bun
|
||||||
uses: actions/setup-node@v2.4.1
|
uses: oven-sh/setup-bun@v1
|
||||||
with:
|
|
||||||
node-version: '14.x'
|
|
||||||
- name: Cache NPM dependencies
|
- name: Cache NPM dependencies
|
||||||
uses: actions/cache@v2.1.6
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: node_modules
|
path: node_modules
|
||||||
key: ${{ runner.os }}-npm-cache
|
key: ${{ runner.os }}-npm-cache
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-npm-cache
|
${{ runner.os }}-npm-cache
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: npm install
|
run: bun install
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: npm run lint
|
run: bun run lint
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
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@v4
|
||||||
|
- run: semgrep ci
|
|
@ -2,21 +2,34 @@ name: Snyk
|
||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
# Every day
|
# Weekly
|
||||||
- cron: '0 0 * * *'
|
- cron: "0 0 * * 0"
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- "dependabot/github_actions/github/codeql-action**"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
security:
|
security:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.3.4
|
- uses: actions/checkout@v4
|
||||||
- name: Use Node.js 14.x
|
- name: Install Bun
|
||||||
uses: actions/setup-node@v2.4.1
|
uses: oven-sh/setup-bun@v1
|
||||||
with:
|
|
||||||
node-version: '14.x'
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: npm install
|
run: bun install
|
||||||
- name: Run Snyk to check for vulnerabilities
|
- 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
|
uses: snyk/actions/node@master
|
||||||
env:
|
env:
|
||||||
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||||
|
|
|
@ -8,22 +8,36 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
node-version: ['12', '14', '16', '17']
|
node-version: ["18", "20", "22"]
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.3.4
|
- uses: actions/checkout@v4
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v2.4.1
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
- name: Cache NPM dependencies
|
- name: Cache NPM dependencies
|
||||||
uses: actions/cache@v2.1.6
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: node_modules
|
path: node_modules
|
||||||
key: ${{ runner.os }}-npm-cache
|
key: ${{ runner.os }}-npm-cache
|
||||||
restore-keys: ${{ runner.os }}-npm-cache
|
restore-keys: ${{ runner.os }}-npm-cache
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: npm install
|
run: npm install
|
||||||
|
- name: Determine zstd 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 zstd binary
|
||||||
|
shell: bash
|
||||||
|
run: npm install "@mongodb-js/zstd-$PLATFORM"
|
||||||
- name: Test
|
- name: Test
|
||||||
run: npm run test
|
run: npm run test
|
||||||
env:
|
env:
|
||||||
|
@ -33,26 +47,30 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest]
|
os: [ubuntu-latest]
|
||||||
node-version: ['14.x']
|
node-version: ["20.x"]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.3.4
|
- uses: actions/checkout@v4
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v2.4.1
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
|
- name: Install Bun
|
||||||
|
uses: oven-sh/setup-bun@v1
|
||||||
- name: Cache NPM dependencies
|
- name: Cache NPM dependencies
|
||||||
uses: actions/cache@v2.1.6
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: node_modules
|
path: node_modules
|
||||||
key: ${{ runner.os }}-npm-cache
|
key: ${{ runner.os }}-npm-cache
|
||||||
restore-keys: ${{ runner.os }}-npm-cache
|
restore-keys: ${{ runner.os }}-npm-cache
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: npm install
|
run: bun install
|
||||||
- name: Coverage
|
- name: Coverage
|
||||||
run: npm run test
|
run: npm run test
|
||||||
env:
|
env:
|
||||||
CI: true
|
CI: true
|
||||||
- name: Upload coverage report to Codecov
|
- name: Upload coverage report to Codecov
|
||||||
uses: codecov/codecov-action@v2.1.0
|
uses: codecov/codecov-action@v4
|
||||||
with:
|
with:
|
||||||
fail_ci_if_error: true
|
fail_ci_if_error: true
|
||||||
|
env:
|
||||||
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
|
@ -4,3 +4,4 @@ package-lock.json
|
||||||
tmp/
|
tmp/
|
||||||
*.log
|
*.log
|
||||||
coverage/
|
coverage/
|
||||||
|
bun.lockb
|
||||||
|
|
6
.snyk
6
.snyk
|
@ -2,8 +2,4 @@
|
||||||
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:
|
||||||
SNYK-JS-LODASH-450202:
|
patch:
|
||||||
- '*':
|
|
||||||
reason: Patch/update unavailable. Introduced through Snyk.
|
|
||||||
expires: '2019-12-31T00:00:00.000Z'
|
|
||||||
patch: {}
|
|
||||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2018-2019 curbengh, 2016-2018 rozbo
|
Copyright (c) 2018-2024 curbengh, 2016-2018 rozbo
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
277
README.md
277
README.md
|
@ -1,68 +1,40 @@
|
||||||
# hexo-yam
|
# hexo-yam
|
||||||
|
|
||||||
[](https://www.npmjs.com/package/hexo-yam)
|
[](https://www.npmjs.com/package/hexo-yam)
|
||||||
[](https://github.com/curbengh/hexo-yam/actions?query=workflow%3ATester)
|
[](https://github.com/curbengh/hexo-yam/actions?query=workflow%3ATester)
|
||||||
[](https://codecov.io/gh/curbengh/hexo-yam)
|
[](https://codecov.io/gh/curbengh/hexo-yam)
|
||||||
[](https://libraries.io/npm/hexo-yam)
|
[](https://libraries.io/npm/hexo-yam)
|
||||||
[](https://snyk.io/test/npm/hexo-yam)
|
[](https://snyk.io/test/github/curbengh/hexo-yam)
|
||||||
[](https://lgtm.com/projects/g/curbengh/hexo-yam/alerts/)
|
|
||||||
[](https://lgtm.com/projects/g/curbengh/hexo-yam/context:javascript)
|
|
||||||
|
|
||||||
Yet Another Minifier for Hexo. Minify and compress HTML, JS, CSS, SVG, XML and JSON. [Other files](https://github.com/curbengh/hexo-yam/blob/ba77db0094a7c07ea9f70f010bfc15541d4105ca/index.js#L64) are also compressed. Support gzip and [brotli](https://en.wikipedia.org/wiki/Brotli) [compressions](https://en.wikipedia.org/wiki/HTTP_compression).
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
## Table of contents
|
## Table of contents
|
||||||
|
|
||||||
- [Version 5](#version-5)
|
|
||||||
- [Installation](#installation)
|
- [Installation](#installation)
|
||||||
- [Options](#options)
|
- [Options](#options)
|
||||||
- [HTML](#html)
|
- [HTML](#html)
|
||||||
- [CSS](#css)
|
- [CSS](#css)
|
||||||
- [JS](#js)
|
- [JS](#js)
|
||||||
- [SVG](#svg)
|
- [SVG](#svg)
|
||||||
- [Gzip](#gzip)
|
|
||||||
- [Brotli](#brotli)
|
|
||||||
- [XML](#xml)
|
- [XML](#xml)
|
||||||
- [JSON](#json)
|
- [JSON](#json)
|
||||||
|
- [Gzip](#gzip)
|
||||||
|
- [Brotli](#brotli)
|
||||||
|
- [Zstd](#zstd)
|
||||||
- [Globbing](#globbing)
|
- [Globbing](#globbing)
|
||||||
- [HTTP Compression](#http-compression)
|
|
||||||
|
|
||||||
## Version 6
|
|
||||||
In v6, `svg.plugins:` option should be the following syntax:
|
|
||||||
|
|
||||||
``` diff
|
|
||||||
minify:
|
|
||||||
svg:
|
|
||||||
plugins:
|
|
||||||
# v6
|
|
||||||
+ removeComments: false
|
|
||||||
+ cleanupIDs: false
|
|
||||||
+ builtinPluginName:
|
|
||||||
+ optionName: 'optionValue'
|
|
||||||
|
|
||||||
# v5
|
|
||||||
- - name: 'removeComments'
|
|
||||||
- active: false
|
|
||||||
- - name: 'cleanupIDs'
|
|
||||||
- active: false
|
|
||||||
|
|
||||||
# v4
|
|
||||||
- - removeComments: false
|
|
||||||
- - cleanupIDs: false
|
|
||||||
```
|
|
||||||
|
|
||||||
The option only overrides svgo's default plugins, other options are not supported.
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
``` bash
|
|
||||||
$ npm install hexo-yam --save
|
```bash
|
||||||
|
$ npm install --save hexo-yam
|
||||||
```
|
```
|
||||||
|
|
||||||
## Options
|
## Options
|
||||||
|
|
||||||
``` yaml
|
```yaml
|
||||||
minify:
|
minify:
|
||||||
enable: true
|
enable: true
|
||||||
|
previewServer: true
|
||||||
html:
|
html:
|
||||||
css:
|
css:
|
||||||
js:
|
js:
|
||||||
|
@ -74,6 +46,7 @@ minify:
|
||||||
```
|
```
|
||||||
|
|
||||||
- **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`.
|
||||||
- **html** - See [HTML](#html) section
|
- **html** - See [HTML](#html) section
|
||||||
- **css** - See [CSS](#css) section
|
- **css** - See [CSS](#css) section
|
||||||
- **js** - See [JS](#js) section
|
- **js** - See [JS](#js) section
|
||||||
|
@ -85,12 +58,13 @@ minify:
|
||||||
|
|
||||||
## HTML
|
## HTML
|
||||||
|
|
||||||
``` yaml
|
```yaml
|
||||||
minify:
|
minify:
|
||||||
html:
|
html:
|
||||||
enable: true
|
enable: true
|
||||||
exclude:
|
exclude:
|
||||||
```
|
```
|
||||||
|
|
||||||
- **enable** - Enable the plugin. Defaults to `true`.
|
- **enable** - Enable the plugin. Defaults to `true`.
|
||||||
- **priority** - Plugin's priority. Defaults to `10`. Set lower value to set higher priority and vice versa.
|
- **priority** - Plugin's priority. Defaults to `10`. Set lower value to set higher priority and vice versa.
|
||||||
- **verbose** - Verbose output. Defaults to `false`.
|
- **verbose** - Verbose output. Defaults to `false`.
|
||||||
|
@ -101,13 +75,14 @@ For more options, see [HTMLMinifier](https://github.com/kangax/html-minifier).
|
||||||
|
|
||||||
## CSS
|
## CSS
|
||||||
|
|
||||||
``` yaml
|
```yaml
|
||||||
minify:
|
minify:
|
||||||
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`.
|
- **priority** - Plugin's priority. Defaults to `10`.
|
||||||
- **verbose** - Verbose output. Defaults to `false`.
|
- **verbose** - Verbose output. Defaults to `false`.
|
||||||
|
@ -119,13 +94,14 @@ For more options, see [clean-css](https://github.com/jakubpawlowicz/clean-css).
|
||||||
|
|
||||||
## JS
|
## JS
|
||||||
|
|
||||||
``` yaml
|
```yaml
|
||||||
minify:
|
minify:
|
||||||
js:
|
js:
|
||||||
enable: true
|
enable: true
|
||||||
exclude:
|
exclude:
|
||||||
- '*.min.js'
|
- "*.min.js"
|
||||||
```
|
```
|
||||||
|
|
||||||
- **enable** - Enable the plugin. Defaults to `true`.
|
- **enable** - Enable the plugin. Defaults to `true`.
|
||||||
- **priority** - Plugin's priority. Defaults to `10`.
|
- **priority** - Plugin's priority. Defaults to `10`.
|
||||||
- **verbose** - Verbose output. Defaults to `false`.
|
- **verbose** - Verbose output. Defaults to `false`.
|
||||||
|
@ -140,14 +116,15 @@ For more options, see [Terser](https://github.com/terser-js/terser).
|
||||||
|
|
||||||
## SVG
|
## SVG
|
||||||
|
|
||||||
``` yaml
|
```yaml
|
||||||
minify:
|
minify:
|
||||||
svg:
|
svg:
|
||||||
enable: true
|
enable: true
|
||||||
include:
|
include:
|
||||||
- '*.svg'
|
- "*.svg"
|
||||||
- '!*.min.svg'
|
- "!*.min.svg"
|
||||||
```
|
```
|
||||||
|
|
||||||
- **enable** - Enable the plugin. Defaults to `true`.
|
- **enable** - Enable the plugin. Defaults to `true`.
|
||||||
- **priority** - Plugin's priority. Defaults to `10`.
|
- **priority** - Plugin's priority. Defaults to `10`.
|
||||||
- **verbose** - Verbose output. Defaults to `false`.
|
- **verbose** - Verbose output. Defaults to `false`.
|
||||||
|
@ -155,82 +132,29 @@ minify:
|
||||||
- Exclude `*.min.svg` by default.
|
- Exclude `*.min.svg` by default.
|
||||||
- **plugins** - Plugin options.
|
- **plugins** - Plugin options.
|
||||||
- Examples:
|
- Examples:
|
||||||
``` yaml
|
```yaml
|
||||||
plugins:
|
plugins:
|
||||||
# Retain comments
|
# Retain comments
|
||||||
removeComments: false
|
removeComments: false
|
||||||
# Do not remove unused ID attributes
|
# Do not remove unused ID attributes
|
||||||
cleanupIDs: false
|
cleanupIds: false
|
||||||
```
|
```
|
||||||
- For more options, see [svgo](https://github.com/svg/svgo).
|
- For more options, see [svgo](https://github.com/svg/svgo).
|
||||||
- **globOptions** - See [globbing](#globbing) section.
|
- **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`.
|
|
||||||
- **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.
|
|
||||||
- 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
|
|
||||||
|
|
||||||
``` yaml
|
|
||||||
minify:
|
|
||||||
brotli:
|
|
||||||
enable: true
|
|
||||||
include:
|
|
||||||
- '*.html'
|
|
||||||
- '*.css'
|
|
||||||
- '*.js'
|
|
||||||
- '*.txt'
|
|
||||||
- '*.ttf'
|
|
||||||
- '*.atom'
|
|
||||||
- '*.stl'
|
|
||||||
- '*.xml'
|
|
||||||
- '*.svg'
|
|
||||||
- '*.eot'
|
|
||||||
- '*.json'
|
|
||||||
```
|
|
||||||
- **enable** - Enable the plugin. Defaults to `true`.
|
|
||||||
- **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)
|
|
||||||
|
|
||||||
## XML
|
## XML
|
||||||
|
|
||||||
Remove whitespaces in xml.
|
Remove whitespaces in xml.
|
||||||
|
|
||||||
``` yaml
|
```yaml
|
||||||
minify:
|
minify:
|
||||||
xml:
|
xml:
|
||||||
enable: false
|
enable: false
|
||||||
include:
|
include:
|
||||||
- '*.xml'
|
- "*.xml"
|
||||||
- '!*.min.xml'
|
- "!*.min.xml"
|
||||||
```
|
```
|
||||||
|
|
||||||
- **enable** - Enable the plugin. Defaults to `false`.
|
- **enable** - Enable the plugin. Defaults to `false`.
|
||||||
- **priority** - Plugin's priority. Defaults to `10`.
|
- **priority** - Plugin's priority. Defaults to `10`.
|
||||||
- **verbose** - Verbose output. Defaults to `false`.
|
- **verbose** - Verbose output. Defaults to `false`.
|
||||||
|
@ -245,14 +169,15 @@ For more options, see [minify-xml](https://github.com/kristian/minify-xml#option
|
||||||
|
|
||||||
Remove whitespaces in json.
|
Remove whitespaces in json.
|
||||||
|
|
||||||
``` yaml
|
```yaml
|
||||||
minify:
|
minify:
|
||||||
json:
|
json:
|
||||||
enable: false
|
enable: false
|
||||||
include:
|
include:
|
||||||
- '*.json'
|
- "*.json"
|
||||||
- '!*.min.json'
|
- "!*.min.json"
|
||||||
```
|
```
|
||||||
|
|
||||||
- **enable** - Enable the plugin. Defaults to `false`.
|
- **enable** - Enable the plugin. Defaults to `false`.
|
||||||
- **priority** - Plugin's priority. Defaults to `10`.
|
- **priority** - Plugin's priority. Defaults to `10`.
|
||||||
- **verbose** - Verbose output. Defaults to `false`.
|
- **verbose** - Verbose output. Defaults to `false`.
|
||||||
|
@ -260,6 +185,101 @@ minify:
|
||||||
- Exclude `*.min.json` by default.
|
- Exclude `*.min.json` by default.
|
||||||
- **globOptions** - See [globbing](#globbing) section.
|
- **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`.
|
||||||
|
- **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.
|
||||||
|
- 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
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
minify:
|
||||||
|
brotli:
|
||||||
|
enable: true
|
||||||
|
include:
|
||||||
|
- "*.html"
|
||||||
|
- "*.css"
|
||||||
|
- "*.js"
|
||||||
|
- "*.txt"
|
||||||
|
- "*.ttf"
|
||||||
|
- "*.atom"
|
||||||
|
- "*.stl"
|
||||||
|
- "*.xml"
|
||||||
|
- "*.svg"
|
||||||
|
- "*.eot"
|
||||||
|
- "*.json"
|
||||||
|
```
|
||||||
|
|
||||||
|
- **enable** - Enable the plugin. Defaults to `true`.
|
||||||
|
- **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
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
minify:
|
||||||
|
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)
|
||||||
|
|
||||||
|
### Cannot find module '@mongodb-js/zstd-linux-x64-gnu'
|
||||||
|
|
||||||
|
`npm install --save @mongodb-js/zstd-linux-x64-gnu`
|
||||||
|
|
||||||
|
- @mongodb-js/zstd-darwin-arm64 (Apple Silicon)
|
||||||
|
- @mongodb-js/zstd-darwin-x64 (Intel Mac)
|
||||||
|
- @mongodb-js/zstd-linux-arm64-gnu
|
||||||
|
- @mongodb-js/zstd-linux-arm64-musl (Alpine)
|
||||||
|
- @mongodb-js/zstd-linux-x64-gnu
|
||||||
|
- @mongodb-js/zstd-linux-x64-musl (Alpine)
|
||||||
|
- @mongodb-js/zstd-win32-x64-msvc
|
||||||
|
|
||||||
## Globbing
|
## Globbing
|
||||||
|
|
||||||
Use "globOptions" to customise how glob patterns match files. Refer to [micromatch](https://github.com/micromatch/micromatch#options) for available options.
|
Use "globOptions" to customise how glob patterns match files. Refer to [micromatch](https://github.com/micromatch/micromatch#options) for available options.
|
||||||
|
@ -268,51 +288,32 @@ Use "globOptions" to customise how glob patterns match files. Refer to [micromat
|
||||||
- basename is disabled depending on each pattern.
|
- basename is disabled depending on each pattern.
|
||||||
- This means the following options would work,
|
- This means the following options would work,
|
||||||
|
|
||||||
``` yml
|
```yml
|
||||||
exclude:
|
exclude:
|
||||||
- '*foo.html' # basename is enabled
|
- "*foo.html" # basename is enabled
|
||||||
- '**/bar/*/*.html' # basename is automatically disabled
|
- "**/bar/*/*.html" # basename is automatically disabled
|
||||||
- '*baz.css' # basename is enabled
|
- "*baz.css" # basename is enabled
|
||||||
globOptions:
|
globOptions:
|
||||||
basename: true # default
|
basename: true # default
|
||||||
```
|
```
|
||||||
|
|
||||||
- This behaviour doesn't apply to pattern that starts with `!` (negation).
|
- 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 limitation only applies to `include:` option used in svg, gzip and brotli.
|
||||||
- This means the following options would *not* work,
|
- This means the following options would _not_ work,
|
||||||
|
|
||||||
``` yml
|
```yml
|
||||||
include:
|
include:
|
||||||
- '!foo.svg'
|
- "!foo.svg"
|
||||||
- '!**/bar/*/*.svg'
|
- "!**/bar/*/*.svg"
|
||||||
globOptions:
|
globOptions:
|
||||||
basename: true
|
basename: true
|
||||||
```
|
```
|
||||||
|
|
||||||
- basename will stay disabled, if explicitly disabled in "globOptions".
|
- basename will stay disabled, if explicitly disabled in "globOptions".
|
||||||
|
|
||||||
|
|
||||||
## HTTP Compression
|
|
||||||
|
|
||||||
While most modern web browsers [support Brotli](https://www.caniuse.com/#feat=brotli), you also need to consider whether the web/app server, hosting platform, reverse proxy or CDN (whichever relevant to you) support it.
|
|
||||||
|
|
||||||
As of Sep 2020, GitHub Pages and GitLab Pages *do not* support brotli yet. You can generate `.br` files, but they won't serve those files.
|
|
||||||
|
|
||||||
Name | Brotli support
|
|
||||||
--- | ---
|
|
||||||
GitHub Pages | [In consideration](https://github.community/t5/GitHub-Pages/Support-for-pre-compressed-assets-and-brotli-compression/m-p/22055)
|
|
||||||
GitLab Pages | Yes
|
|
||||||
Netlify | Yes
|
|
||||||
Hexo Server | [In progress](https://github.com/hexojs/hexo-server/pull/100)
|
|
||||||
|
|
||||||
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),
|
|
||||||
|
|
||||||
- [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).
|
|
||||||
- [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. v2 requires [manual configuration](https://mdleom.com/blog/2020/11/12/caddy2-pre-compressed/), serving them automatically to be supported [in future](https://github.com/caddyserver/caddy/issues/2665).
|
|
||||||
- [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.
|
|
||||||
|
|
||||||
## 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
|
||||||
- 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)
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
[install]
|
||||||
|
optional = false
|
||||||
|
|
||||||
|
[install.lockfile]
|
||||||
|
save = false
|
14
index.js
14
index.js
|
@ -2,7 +2,8 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
hexo.config.minify = Object.assign({
|
hexo.config.minify = Object.assign({
|
||||||
enable: true
|
enable: true,
|
||||||
|
previewServer: true
|
||||||
}, hexo.config.minify)
|
}, hexo.config.minify)
|
||||||
|
|
||||||
hexo.config.minify.html = Object.assign({
|
hexo.config.minify.html = Object.assign({
|
||||||
|
@ -68,6 +69,14 @@ hexo.config.minify.brotli = Object.assign({
|
||||||
globOptions: { basename: true }
|
globOptions: { basename: true }
|
||||||
}, hexo.config.minify.brotli)
|
}, 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({
|
hexo.config.minify.xml = Object.assign({
|
||||||
enable: false,
|
enable: false,
|
||||||
priority: 10,
|
priority: 10,
|
||||||
|
@ -85,7 +94,7 @@ hexo.config.minify.json = Object.assign({
|
||||||
globOptions: { basename: true }
|
globOptions: { basename: true }
|
||||||
}, hexo.config.minify.json)
|
}, hexo.config.minify.json)
|
||||||
|
|
||||||
if (hexo.config.minify.enable === true) {
|
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.minifyHtml, hexo.config.minify.html.priority)
|
||||||
hexo.extend.filter.register('after_render:css', filter.minifyCss, hexo.config.minify.css.priority)
|
hexo.extend.filter.register('after_render:css', filter.minifyCss, hexo.config.minify.css.priority)
|
||||||
|
@ -93,6 +102,7 @@ if (hexo.config.minify.enable === true) {
|
||||||
hexo.extend.filter.register('after_generate', filter.minifySvg, hexo.config.minify.svg.priority)
|
hexo.extend.filter.register('after_generate', filter.minifySvg, hexo.config.minify.svg.priority)
|
||||||
hexo.extend.filter.register('after_generate', filter.gzipFn, hexo.config.minify.gzip.priority)
|
hexo.extend.filter.register('after_generate', filter.gzipFn, hexo.config.minify.gzip.priority)
|
||||||
hexo.extend.filter.register('after_generate', filter.brotliFn, hexo.config.minify.brotli.priority)
|
hexo.extend.filter.register('after_generate', filter.brotliFn, hexo.config.minify.brotli.priority)
|
||||||
|
hexo.extend.filter.register('after_generate', filter.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.minifyXml, hexo.config.minify.xml.priority)
|
||||||
hexo.extend.filter.register('after_generate', filter.minifyJson, hexo.config.minify.json.priority)
|
hexo.extend.filter.register('after_generate', filter.minifyJson, hexo.config.minify.json.priority)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const { minify: htmlMinify } = require('html-minifier')
|
const { minify: htmlMinify } = require('html-minifier-terser')
|
||||||
const CleanCSS = require('clean-css')
|
const CleanCSS = require('clean-css')
|
||||||
const { minify: terserMinify } = require('terser')
|
const { minify: terserMinify } = require('terser')
|
||||||
const { optimize: svgOptimize } = require('svgo')
|
const { optimize: svgOptimize } = require('svgo')
|
||||||
|
@ -10,6 +10,7 @@ const gzip = promisify(zlib.gzip)
|
||||||
const br = promisify(zlib.brotliCompress)
|
const br = promisify(zlib.brotliCompress)
|
||||||
const { minify: compressXml } = require('minify-xml')
|
const { minify: compressXml } = require('minify-xml')
|
||||||
const micromatch = require('micromatch')
|
const micromatch = require('micromatch')
|
||||||
|
const { compress: zstd } = require('@mongodb-js/zstd')
|
||||||
|
|
||||||
const isMatch = (path = '', patterns = [], options = {}) => {
|
const isMatch = (path = '', patterns = [], options = {}) => {
|
||||||
if (path && patterns) {
|
if (path && patterns) {
|
||||||
|
@ -59,7 +60,7 @@ function logFn (original, minified, path, ext) {
|
||||||
log.log(`${ext}: ${path} [${saved}% saved]`)
|
log.log(`${ext}: ${path} [${saved}% saved]`)
|
||||||
}
|
}
|
||||||
|
|
||||||
function minifyHtml (str, data) {
|
async function minifyHtml (str, data) {
|
||||||
const hexo = this
|
const hexo = this
|
||||||
const options = hexo.config.minify.html
|
const options = hexo.config.minify.html
|
||||||
if (options.enable === false || !str) return
|
if (options.enable === false || !str) return
|
||||||
|
@ -71,7 +72,7 @@ function minifyHtml (str, data) {
|
||||||
if (isMatch(path, exclude, globOptions)) return str
|
if (isMatch(path, exclude, globOptions)) return str
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = htmlMinify(str, options)
|
const result = await htmlMinify(str, options)
|
||||||
if (verbose) logFn.call(this, str, result, path, 'html')
|
if (verbose) logFn.call(this, str, result, path, 'html')
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
@ -152,12 +153,12 @@ function minifySvg () {
|
||||||
assetPath.on('data', (chunk) => (assetTxt += chunk))
|
assetPath.on('data', (chunk) => (assetTxt += chunk))
|
||||||
assetPath.on('end', async () => {
|
assetPath.on('end', async () => {
|
||||||
if (assetTxt.length) {
|
if (assetTxt.length) {
|
||||||
const { data, error } = svgOptimize(assetTxt, { ...options, plugins })
|
try {
|
||||||
if (data) {
|
const { data } = svgOptimize(assetTxt, { ...options, plugins })
|
||||||
if (verbose) logFn.call(this, assetTxt, data, path, 'svg')
|
if (verbose) logFn.call(this, assetTxt, data, path, 'svg')
|
||||||
resolve(route.set(path, data))
|
resolve(route.set(path, data))
|
||||||
} else if (error) {
|
} catch (err) {
|
||||||
reject(new Error(`Path: ${path}\n${error}`))
|
reject(new Error(`Path: ${path}\n${err}`))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resolve()
|
resolve()
|
||||||
|
@ -230,6 +231,39 @@ function brotliFn () {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 () {
|
function minifyXml () {
|
||||||
const hexo = this
|
const hexo = this
|
||||||
const options = hexo.config.minify.xml
|
const options = hexo.config.minify.xml
|
||||||
|
@ -297,6 +331,7 @@ module.exports = {
|
||||||
minifySvg,
|
minifySvg,
|
||||||
gzipFn,
|
gzipFn,
|
||||||
brotliFn,
|
brotliFn,
|
||||||
|
zstdFn,
|
||||||
minifyXml,
|
minifyXml,
|
||||||
minifyJson
|
minifyJson
|
||||||
}
|
}
|
||||||
|
|
29
package.json
29
package.json
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"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, css, svg, xml and json",
|
||||||
"version": "6.0.0",
|
"version": "9.0.0",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"directories": {
|
"directories": {
|
||||||
|
@ -16,36 +16,35 @@
|
||||||
"test": "jest"
|
"test": "jest"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.13.0"
|
"node": ">= 18.12.0"
|
||||||
},
|
},
|
||||||
"author": "curben",
|
"author": "curben",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"homepage": "https://github.com/curbengh/hexo-yam",
|
"homepage": "https://github.com/curbengh/hexo-yam",
|
||||||
"repository": "curbengh/hexo-yam",
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/curbengh/hexo-yam.git"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"clean-css": "^5.1.2",
|
"clean-css": "^5.1.2",
|
||||||
"html-minifier": "^4.0.0",
|
"html-minifier-terser": "^7.2.0",
|
||||||
"micromatch": "^4.0.2",
|
"micromatch": "^4.0.2",
|
||||||
"minify-xml": "^3.2.0",
|
"minify-xml": "^3.2.0",
|
||||||
"svgo": "^2.4.0",
|
"svgo": "^3.0.0",
|
||||||
"terser": "^5.3.0"
|
"terser": "^5.3.0",
|
||||||
|
"@mongodb-js/zstd": "^1.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"hexo": "^5.1.1",
|
"hexo": "^7.1.0",
|
||||||
"jest": "^27.2.5",
|
"jest": "^29.1.2",
|
||||||
"standard": "^16.0.1"
|
"standard": "^17.0.0"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"html",
|
|
||||||
"js",
|
|
||||||
"css",
|
|
||||||
"svg",
|
|
||||||
"xml",
|
|
||||||
"json",
|
|
||||||
"minify",
|
"minify",
|
||||||
"compress",
|
"compress",
|
||||||
"gzip",
|
"gzip",
|
||||||
"brotli",
|
"brotli",
|
||||||
|
"zstd",
|
||||||
"hexo-yam",
|
"hexo-yam",
|
||||||
"hexo"
|
"hexo"
|
||||||
],
|
],
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const Hexo = require('hexo')
|
const Hexo = require('hexo')
|
||||||
const { minify: htmlMinify } = require('html-minifier')
|
const { minify: htmlMinify } = require('html-minifier-terser')
|
||||||
|
|
||||||
describe('html', () => {
|
describe('html', () => {
|
||||||
const hexo = new Hexo(__dirname)
|
const hexo = new Hexo(__dirname)
|
||||||
|
@ -26,105 +26,107 @@ describe('html', () => {
|
||||||
globOptions: { basename: true }
|
globOptions: { basename: true }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const expected = htmlMinify(input, defaultCfg.html)
|
let expected = ''
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
expected = await htmlMinify(input, defaultCfg.html)
|
||||||
|
})
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
hexo.config.minify = JSON.parse(JSON.stringify(defaultCfg))
|
hexo.config.minify = JSON.parse(JSON.stringify(defaultCfg))
|
||||||
})
|
})
|
||||||
|
|
||||||
test('default', () => {
|
test('default', async () => {
|
||||||
const result = h(input, { path })
|
const result = await h(input, { path })
|
||||||
|
|
||||||
expect(result).toBe(expected)
|
expect(result).toBe(expected)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('disable', () => {
|
test('disable', async () => {
|
||||||
hexo.config.minify.html.enable = false
|
hexo.config.minify.html.enable = false
|
||||||
|
|
||||||
const result = h(input, { path })
|
const result = await h(input, { path })
|
||||||
|
|
||||||
expect(result).toBeUndefined()
|
expect(result).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('empty file', () => {
|
test('empty file', async () => {
|
||||||
const result = h('', { path })
|
const result = await h('', { path })
|
||||||
|
|
||||||
expect(result).toBeUndefined()
|
expect(result).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('option', () => {
|
test('option', async () => {
|
||||||
const customOpt = { removeEmptyAttributes: false }
|
const customOpt = { removeEmptyAttributes: false }
|
||||||
hexo.config.minify.html = customOpt
|
hexo.config.minify.html = customOpt
|
||||||
|
|
||||||
const result = h(input, { path })
|
const result = await h(input, { path })
|
||||||
const expected = htmlMinify(input, customOpt)
|
const expected = await htmlMinify(input, customOpt)
|
||||||
|
|
||||||
expect(result).toBe(input)
|
expect(result).toBe(input)
|
||||||
expect(result).toBe(expected)
|
expect(result).toBe(expected)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('option - verbose', () => {
|
test('option - verbose', async () => {
|
||||||
hexo.config.minify.html.verbose = true
|
hexo.config.minify.html.verbose = true
|
||||||
hexo.log.log = jest.fn()
|
hexo.log.log = jest.fn()
|
||||||
h(input, { path })
|
await h(input, { path })
|
||||||
|
|
||||||
expect(hexo.log.log.mock.calls[0][0]).toContain(`html: ${path}`)
|
expect(hexo.log.log.mock.calls[0][0]).toContain(`html: ${path}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('exclude', () => {
|
test('exclude', async () => {
|
||||||
const exclude = '*.min.html'
|
const exclude = '*.min.html'
|
||||||
hexo.config.minify.html.exclude = exclude
|
hexo.config.minify.html.exclude = exclude
|
||||||
|
|
||||||
const result = h(input, { path: 'foo/bar.min.html' })
|
const result = await h(input, { path: 'foo/bar.min.html' })
|
||||||
|
|
||||||
expect(result).toBe(input)
|
expect(result).toBe(input)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('exclude - slash in pattern', () => {
|
test('exclude - slash in pattern', async () => {
|
||||||
const exclude = '**/lectus/**/*.html'
|
const exclude = '**/lectus/**/*.html'
|
||||||
hexo.config.minify.html.exclude = exclude
|
hexo.config.minify.html.exclude = exclude
|
||||||
|
|
||||||
const result = h(input, { path: 'eleifend/lectus/nullam/dapibus/netus.html' })
|
const result = await h(input, { path: 'eleifend/lectus/nullam/dapibus/netus.html' })
|
||||||
|
|
||||||
expect(result).toBe(input)
|
expect(result).toBe(input)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('exclude - basename is true + slash', () => {
|
test('exclude - basename is true + slash', async () => {
|
||||||
const exclude = ['**/lectus/**/*.html', 'bar.html']
|
const exclude = ['**/lectus/**/*.html', 'bar.html']
|
||||||
const globOptions = { basename: true }
|
const globOptions = { basename: true }
|
||||||
hexo.config.minify.html.exclude = exclude
|
hexo.config.minify.html.exclude = exclude
|
||||||
hexo.config.minify.html.globOptions = globOptions
|
hexo.config.minify.html.globOptions = globOptions
|
||||||
|
|
||||||
const result = h(input, { path: 'foo/bar.html' })
|
const result = await h(input, { path: 'foo/bar.html' })
|
||||||
|
|
||||||
expect(result).toBe(input)
|
expect(result).toBe(input)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('exclude - basename is false + slash', () => {
|
test('exclude - basename is false + slash', async () => {
|
||||||
const exclude = ['**/lectus/**/*.html', 'bar.html']
|
const exclude = ['**/lectus/**/*.html', 'bar.html']
|
||||||
const globOptions = { basename: false }
|
const globOptions = { basename: false }
|
||||||
hexo.config.minify.html.exclude = exclude
|
hexo.config.minify.html.exclude = exclude
|
||||||
hexo.config.minify.html.globOptions = globOptions
|
hexo.config.minify.html.globOptions = globOptions
|
||||||
|
|
||||||
const result = h(input, { path: 'foo/bar.html' })
|
const result = await h(input, { path: 'foo/bar.html' })
|
||||||
|
|
||||||
expect(result).toBe(expected)
|
expect(result).toBe(expected)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('null', () => {
|
test('null', async () => {
|
||||||
hexo.config.minify.html.exclude = null
|
hexo.config.minify.html.exclude = null
|
||||||
hexo.config.minify.html.globOptions = null
|
hexo.config.minify.html.globOptions = null
|
||||||
|
|
||||||
const result = h(input, { path: null })
|
const result = await h(input, { path: null })
|
||||||
|
|
||||||
expect(result).toBe(expected)
|
expect(result).toBe(expected)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('invalid string', () => {
|
test('invalid string', async () => {
|
||||||
const invalid = '<html><>?:"{}|_+</html>'
|
const invalid = '<html><>?:"{}|_+</html>'
|
||||||
|
|
||||||
expect(() => {
|
await expect(h(invalid, { path })).rejects.toThrow('Parse Error: <>?:"{}|_+</html>')
|
||||||
h(invalid, { path })
|
|
||||||
}).toThrow(`Path: ${path}\nError: Parse Error`)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -71,7 +71,7 @@ describe('svg', () => {
|
||||||
|
|
||||||
test('option', async () => {
|
test('option', async () => {
|
||||||
const customOpt = {
|
const customOpt = {
|
||||||
cleanupIDs: false
|
cleanupIds: false
|
||||||
}
|
}
|
||||||
hexo.config.minify.svg.plugins = customOpt
|
hexo.config.minify.svg.plugins = customOpt
|
||||||
plugins = [{
|
plugins = [{
|
||||||
|
@ -117,10 +117,14 @@ describe('svg', () => {
|
||||||
const input = '{}'
|
const input = '{}'
|
||||||
hexo.route.set(path, input)
|
hexo.route.set(path, input)
|
||||||
|
|
||||||
const { error } = svgOptimize(input, { plugins })
|
let expected
|
||||||
|
try {
|
||||||
expect(error).toBeDefined()
|
svgOptimize(input, { plugins })
|
||||||
await expect(s()).rejects.toThrow(`Path: ${path}\n${error}`)
|
} catch (err) {
|
||||||
|
expected = err
|
||||||
|
}
|
||||||
|
expect(expected).toBeDefined()
|
||||||
|
await expect(s()).rejects.toThrow(`Path: ${path}\n${expected}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('include - exclude *.min.svg by default', async () => {
|
test('include - exclude *.min.svg by default', async () => {
|
||||||
|
|
|
@ -0,0 +1,256 @@
|
||||||
|
/* 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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in New Issue