Compare commits

...

250 Commits

Author SHA1 Message Date
MDLeom 4bd000bfee
release: 9.0.0 2024-06-22 02:28:47 +00:00
MDLeom 5fb3349dec
chore(npm): add "zstd" keyword
remove generic keywords
2024-06-22 02:26:43 +00:00
MDLeom 80b2325cbf
chore(npm): "repository" is now an object 2024-06-22 02:25:33 +00:00
MDLeom 812ff614fc
ci: use windows bash
variable is not expanded properly in pwsh
2024-06-11 12:37:53 +00:00
MDLeom 702eb2fa0c
ci: ${} not supported in pwsh 2024-06-11 12:22:08 +00:00
MDLeom a2c810cfb5
ci: install zstd binary 2024-06-11 12:06:41 +00:00
MDLeom 30c33c0ad3
docs: install zstd dependency explicitly
using --force --include=optional flags are excessive
This reverts commit e33fd0ed5d &
1b04f7c8f1.
2024-06-11 11:30:02 +00:00
MDLeom 1c9d91b524
docs(license): update year 2024-06-10 06:19:21 +00:00
MDLeom c18f4c8476
docs: differentiate minify and compress
update snyk badge
2024-06-10 06:18:06 +00:00
MDLeom e33fd0ed5d
build(npm): --force flag to install optional deps
of child deps
required by https://github.com/curbengh/hexo-yam#cannot-find-module-mongodb-jszstd-linux-x64-gnu
2024-06-08 08:11:38 +00:00
MDLeom 73db847fd4
docs: reorder sections
removed outdated http-compression section
2024-06-08 08:11:28 +00:00
Ming Di Leom 7639547408
Merge pull request #157 from curbengh/previewServer
feat: previewServer option
2024-06-07 20:06:54 +09:30
Ming Di Leom f7df26377a
Merge pull request #158 from curbengh/zstd
feat: zstd compression
2024-06-07 19:52:50 +09:30
MDLeom 1b04f7c8f1
docs(zstd): --force flag to install optional deps
of child deps
https://stackoverflow.com/a/42400102
2024-06-07 09:04:05 +00:00
MDLeom 18a5d8108b
test: include optional deps
to include zstd binary
2024-06-06 11:45:53 +00:00
MDLeom 7f2033e553
feat: zstd compression
disabled by default due to lack of server support
close #156
2024-06-06 11:14:57 +00:00
MDLeom e9584b2e0b
feat: previewServer option
BREAKING CHANGE
plugin is now disabled by default when running `hexo server`,
unless previewServer is set to false.

Close #154
Inspired by e778c55f37/index.js (L49)
2024-06-05 11:08:12 +00:00
Ming Di Leom e99e110d39
Merge pull request #155 from curbengh/dependabot/github_actions/codecov/codecov-action-4
chore(deps): bump codecov/codecov-action from 3 to 4
2024-06-05 18:46:36 +09:30
MDLeom a5f19dbe79
ci(codecov): add upload token
https://github.com/codecov/codecov-action/releases/tag/v4.0.0
2024-06-05 09:14:12 +00:00
MDLeom c3ed0c68c7
test: node 22
drop 21, EOL on 2024-06-01
2024-06-05 09:04:31 +00:00
MDLeom f92017f688
test: node 22
drop 21, EOL on 2024-06-01
2024-06-04 08:52:45 +00:00
dependabot[bot] 0abd90c78a
chore(deps): bump codecov/codecov-action from 3 to 4
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3 to 4.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-01 15:46:21 +00:00
Ming Di Leom 426da85620
Merge pull request #150 from curbengh/dependabot/github_actions/actions/cache-4
chore(deps): bump actions/cache from 3 to 4
2024-01-28 21:14:53 +10:30
Ming Di Leom bfb886abac
Merge pull request #149 from curbengh/dependabot/github_actions/github/codeql-action-3
chore(deps): bump github/codeql-action from 2 to 3
2024-01-28 21:12:39 +10:30
Ming Di Leom d99caafe90
Merge pull request #145 from curbengh/dependabot/github_actions/actions/setup-node-4
chore(deps): bump actions/setup-node from 3 to 4
2024-01-28 21:10:18 +10:30
Ming Di Leom 3cc6f5e79e
Merge pull request #151 from curbengh/dependabot/npm_and_yarn/hexo-7.1.0
chore(deps-dev): bump hexo from 6.3.0 to 7.1.0
2024-01-28 21:06:37 +10:30
Ming Di Leom 56af48f632
Merge pull request #152 from curbengh/html-minifier-terser
refactor: switch html-minifier to html-minifier-terser
2024-01-28 21:04:53 +10:30
MDLeom 0bee9ada0e
refactor: switch html-minifier to html-minifier-terser
https://github.com/kangax/html-minifier/issues/1135
2024-01-28 10:26:21 +00:00
dependabot[bot] 1af8c52cdb
chore(deps-dev): bump hexo from 6.3.0 to 7.1.0
Bumps [hexo](https://github.com/hexojs/hexo) from 6.3.0 to 7.1.0.
- [Release notes](https://github.com/hexojs/hexo/releases)
- [Commits](https://github.com/hexojs/hexo/compare/6.3.0...v7.1.0)

---
updated-dependencies:
- dependency-name: hexo
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-19 15:40:47 +00:00
dependabot[bot] 81ceea413d
chore(deps): bump actions/cache from 3 to 4
Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-18 15:37:36 +00:00
dependabot[bot] 8e937ead21
chore(deps): bump github/codeql-action from 2 to 3
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2 to 3.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-13 15:52:54 +00:00
MDLeom 7809098034
ci: node 20
active lts
2023-12-12 08:33:33 +00:00
MDLeom dffeb0cfc7
test: node 21 2023-12-12 08:30:27 +00:00
dependabot[bot] bfcd93edb7
chore(deps): bump actions/setup-node from 3 to 4
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 3 to 4.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-24 15:47:02 +00:00
Ming Di Leom 2992e8d8e7
Merge pull request #142 from curbengh/dependabot/github_actions/actions/checkout-4
chore(deps): bump actions/checkout from 3 to 4
2023-10-01 15:20:29 +10:30
dependabot[bot] cf7e8f675c
chore(deps): bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-01 04:48:01 +00:00
MDLeom 3523b0fd98
chore(node): drop Node 16
BREAKING CHANGE
Node 16 EOL 2023-09-11
2023-10-01 04:46:39 +00:00
Ming Di Leom df3b0513e7
Merge pull request #143 from curbengh/bun
feat(ci): run linux jobs using Bun
2023-10-01 15:09:17 +10:30
MDLeom dbed65ffeb
feat(ci): run linux jobs using Bun 2023-10-01 04:05:08 +00:00
Ming Di Leom c964cfcf80
Merge pull request #140 from curbengh/node-20
chore(node): require node 16+
2023-10-01 14:06:48 +10:30
MDLeom 793bcd81c4
chore(node): require node 16+
- node 14 EOL 2023-04-30
- add node 20
2023-05-06 06:04:21 +00:00
MDLeom f65e7e7a22
docs: remove deprecated lgtm badge 2023-04-28 08:53:38 +00:00
MDLeom d9e6228960
docs: fix build badge
https://github.com/badges/shields/issues/8671
2023-03-06 09:33:04 +00:00
Ming Di Leom 6c6060f28c
Merge pull request #139 from curbengh/8.0
release: 8.0.0
2022-11-13 11:08:42 +10:30
MDLeom 79f4f24f8e
release: 8.0.0 2022-11-13 00:05:34 +00:00
Ming Di Leom b0575e6fdc
Merge pull request #135 from curbengh/dependabot/npm_and_yarn/svgo-3.0.0
chore(deps): bump svgo from 2.8.0 to 3.0.0
2022-11-06 15:55:40 +10:30
MDLeom a6f434ff8d
fix(svg): ignore empty input 2022-11-06 04:50:36 +00:00
MDLeom a6e2ea0879
fix(svg): svgo@3 now throws error
- cleanupIDs -> cleanupIds
- https://github.com/svg/svgo/releases/tag/v3.0.0
2022-11-06 02:58:07 +00:00
dependabot[bot] 9c8a228e09
chore(deps): bump svgo from 2.8.0 to 3.0.0
Bumps [svgo](https://github.com/svg/svgo) from 2.8.0 to 3.0.0.
- [Release notes](https://github.com/svg/svgo/releases)
- [Changelog](https://github.com/svg/svgo/blob/main/CHANGELOG-old.md)
- [Commits](https://github.com/svg/svgo/compare/v2.8.0...v3.0.0)

---
updated-dependencies:
- dependency-name: svgo
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-06 02:58:07 +00:00
Ming Di Leom 4b4d3cb342
Merge pull request #137 from curbengh/node-19
ci(tester): add node 19
2022-11-06 13:26:09 +10:30
MDLeom 8258359f6b
ci(tester): add node 19 2022-11-05 02:02:16 +00:00
Ming Di Leom c6c7d3743a
Merge pull request #134 from curbengh/deps
chore(ga-deps): use latest minor version
2022-10-09 19:53:39 +10:30
MDLeom 777ffabd2b
ci: run snyk & semgrep weekly 2022-10-09 08:00:56 +00:00
MDLeom 5453fc9ee0
chore(deps-dev): bump jest from 28.1.3 to 29.1.2 2022-10-09 07:59:47 +00:00
MDLeom c3b899556a
chore(ga-deps): use latest minor version 2022-10-09 07:58:57 +00:00
Ming Di Leom 866ba28c3c
Merge pull request #133 from curbengh/semgrep-init
ci: semgrep init
2022-10-09 18:09:46 +10:30
MDLeom b54324c154
ci: semgrep init 2022-10-09 07:32:00 +00:00
MDLeom 077055ed0b
release: 7.0.0 2022-05-17 00:59:09 +00:00
MDLeom d54ed360be
chore(node): use Node 16 on codeql 2022-05-17 00:40:28 +00:00
MDLeom 1ae4d77071
chore(dev-deps): bump setup-node from 3.1.1 to 3.2.0 2022-05-17 00:37:59 +00:00
MDLeom 0f73e6ed98
chore(dev-deps): bump codeql-action from 1 to 2 2022-05-17 00:36:57 +00:00
MDLeom 79f15acb79
ci(snyk): run snyk on codeql bump 2022-05-17 00:35:52 +00:00
MDLeom 2ae5520a60
ci(snyk): separate test & monitor 2022-05-13 01:27:05 +00:00
MDLeom ef46c3c6d9
ci(synk): run test & upload to codeql 2022-05-13 01:00:45 +00:00
MDLeom 4648fa32f1
chore(snyk): remove old policy 2022-05-13 00:50:47 +00:00
MDLeom 197836b82b
docs: cleanup previous config changes
- update brotli support status
2022-05-06 06:21:15 +00:00
Ming Di Leom d757c1b5c7
Merge pull request #110 from curbengh/update-deps
chore(node): drop Node 12 & 17, add 18
2022-05-05 14:24:05 +09:30
MDLeom 0623de5252
chore(node): drop Node 12 & 17, add 18
BREAKING CHANGE
2022-05-04 03:17:49 +00:00
MDLeom fbb2c71e87
chore(actions): bump codecov-action from 2.1.0 to 3.1.0 2022-05-04 02:38:28 +00:00
MDLeom b3d09fec7a
chore(actions): bump setup-node from 2.4.1 to 3.1.1 2022-05-04 02:36:40 +00:00
MDLeom 3edbe68008
chore(actions): bump cache from 2.1.6 to 3.0.2 2022-05-04 02:35:32 +00:00
MDLeom 1833ffa76e
chore(actions): bump checkout from 2.3.4 to 3.0.2 2022-05-04 02:35:02 +00:00
MDLeom 63eee78e01
chore(dev-deps): bump standard from 16 to 17 2022-05-04 02:29:34 +00:00
MDLeom 7e81770894
chore(dev-deps): bump jest from 27 to 28 2022-05-04 02:28:59 +00:00
MDLeom 288a4e3232
chore(dev-deps): bump hexo from 5.1.1 to 6.1.0 2022-05-04 02:28:14 +00:00
MDLeom d8e6969352
release: 6.0.0 2021-10-20 09:02:30 +00:00
Ming Di Leom a6018cb7f5
Merge pull request #84 from curbengh/node-17
chore(ci): Node 17
2021-10-20 19:31:01 +10:30
MDLeom 4a271929d5
chore(ci): Node 17 2021-10-20 08:56:46 +00:00
MDLeom 282a905305
chore(ci): drop Node 15
- EOL 2021-06-01
2021-10-16 09:54:49 +00:00
MDLeom 5f9c3d32d9 chore(deps): bump svgo from 2.3.0 to 2.4.0
- tested compatibility
2021-10-16 09:38:02 +00:00
Ming Di Leom 6d633f0593
Merge pull request #82 from curbengh/dependabot/npm_and_yarn/jest-27.2.5
chore(deps-dev): bump jest from 26.6.3 to 27.2.5
2021-10-16 20:06:54 +10:30
Ming Di Leom 0e66fc81cf
Merge pull request #78 from curbengh/dependabot/github_actions/actions/setup-node-2.4.1
chore(deps): bump actions/setup-node from 2.1.5 to 2.4.1
2021-10-16 20:04:45 +10:30
Ming Di Leom 7178b50186
Merge pull request #75 from curbengh/dependabot/github_actions/codecov/codecov-action-2.1.0
chore(deps): bump codecov/codecov-action from 1 to 2.1.0
2021-10-16 20:03:55 +10:30
Ming Di Leom cc53b9054b
Merge pull request #71 from curbengh/dependabot/npm_and_yarn/minify-xml-3.2.0
chore(deps): bump minify-xml from 2.5.0 to 3.2.0
2021-10-16 20:01:41 +10:30
dependabot[bot] 3ce4b69991
chore(deps): bump actions/setup-node from 2.1.5 to 2.4.1
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 2.1.5 to 2.4.1.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v2.1.5...v2.4.1)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-16 09:29:53 +00:00
Ming Di Leom 1f775df118
Merge pull request #58 from curbengh/dependabot/github_actions/actions/cache-2.1.6
chore(deps): bump actions/cache from 2.1.4 to 2.1.6
2021-10-16 19:59:45 +10:30
Ming Di Leom 45ed8c2bac
Merge pull request #54 from curbengh/dependabot/github_actions/actions/checkout-2.3.4
chore(deps): bump actions/checkout from 2 to 2.3.4
2021-10-16 19:59:02 +10:30
MDLeom c0041b0f23
fix(svg): compatibility with svgo 2.4+
- overriding default plugins
- https://github.com/svg/svgo/releases/tag/v2.4.0
2021-10-10 09:19:50 +00:00
dependabot[bot] 88deed06bb
chore(deps-dev): bump jest from 26.6.3 to 27.2.5
Bumps [jest](https://github.com/facebook/jest) from 26.6.3 to 27.2.5.
- [Release notes](https://github.com/facebook/jest/releases)
- [Changelog](https://github.com/facebook/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/jest/compare/v26.6.3...v27.2.5)

---
updated-dependencies:
- dependency-name: jest
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-08 15:05:13 +00:00
dependabot[bot] ed5d09c803
chore(deps): bump codecov/codecov-action from 1 to 2.1.0
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 1 to 2.1.0.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/master/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v1...v2.1.0)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-13 15:02:16 +00:00
dependabot[bot] e4b349202c
chore(deps): bump minify-xml from 2.5.0 to 3.2.0
Bumps [minify-xml](https://github.com/kristian/minify-xml) from 2.5.0 to 3.2.0.
- [Release notes](https://github.com/kristian/minify-xml/releases)
- [Commits](https://github.com/kristian/minify-xml/compare/2.5.0...3.2.0)

---
updated-dependencies:
- dependency-name: minify-xml
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-23 15:04:15 +00:00
dependabot[bot] 460bb9c035
chore(deps): bump actions/cache from 2.1.4 to 2.1.6
Bumps [actions/cache](https://github.com/actions/cache) from 2.1.4 to 2.1.6.
- [Release notes](https://github.com/actions/cache/releases)
- [Commits](https://github.com/actions/cache/compare/v2.1.4...v2.1.6)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-28 05:22:05 +00:00
dependabot[bot] eca9b2b7c9
chore(deps): bump actions/checkout from 2 to 2.3.4
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 2.3.4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v2.3.4)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-12 05:09:55 +00:00
Ming Di Leom be96f15ceb
Merge pull request #52 from curbengh/node-16
release: 5.0.0
2021-05-02 15:45:10 +09:30
MDLeom fde28cdd9a
release: 5.0.0 2021-04-25 08:51:39 +00:00
MDLeom 7aa2f90e52
chore(ci): add Node 16
- released 2021-04-20
2021-04-25 08:48:55 +00:00
MDLeom b169489ce9
chore(ci): run most tests on Node 14
- active LTS
2021-04-25 08:47:04 +00:00
MDLeom 81fd44e514
chore: drop Node 10
- EOL 2021-04-30
2021-04-25 08:45:39 +00:00
MDLeom 1f95af32b7
docs(v5): syntax change in svg.plugins option 2021-04-25 08:41:23 +00:00
Ming Di Leom 6c21cc6a9b
Merge pull request #51 from curbengh/svgo-2
chore(deps): bump svgo from 1.3.2 to 2.3.0 (BREAKING CHANGE)
2021-04-25 18:04:24 +09:30
MDLeom 3819909c84
chore(deps): bump svgo from 1.3.2 to 2.3.0 (BREAKING CHANGE)
- BREAKING CHANGE: syntax of minify.svg.plugins option
  * https://github.com/svg/svgo/releases/tag/v2.0.0
- svgo optimize() changed from async to sync
2021-04-25 08:25:32 +00:00
Ming Di Leom 4f033711e2
Merge pull request #49 from curbengh/dependabot/npm_and_yarn/clean-css-5.1.2
chore(deps): bump clean-css from 4.2.3 to 5.1.2
2021-04-25 15:53:44 +09:30
Ming Di Leom e05f96f971
Merge pull request #42 from curbengh/dependabot/github_actions/actions/setup-node-v2.1.5
chore(deps): bump actions/setup-node from v2.1.2 to v2.1.5
2021-04-25 15:51:56 +09:30
Ming Di Leom 193cc355be
Merge pull request #37 from curbengh/dependabot/github_actions/actions/cache-v2.1.4
chore(deps): bump actions/cache from v2 to v2.1.4
2021-04-25 15:51:30 +09:30
dependabot[bot] 679858e4e8
chore(deps): bump clean-css from 4.2.3 to 5.1.2
Bumps [clean-css](https://github.com/jakubpawlowicz/clean-css) from 4.2.3 to 5.1.2.
- [Release notes](https://github.com/jakubpawlowicz/clean-css/releases)
- [Changelog](https://github.com/jakubpawlowicz/clean-css/blob/master/History.md)
- [Commits](https://github.com/jakubpawlowicz/clean-css/compare/v4.2.3...v5.1.2)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-22 06:55:41 +00:00
dependabot[bot] 1c10c9ee5e
chore(deps): bump actions/setup-node from v2.1.2 to v2.1.5
Bumps [actions/setup-node](https://github.com/actions/setup-node) from v2.1.2 to v2.1.5.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v2.1.2...46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea)

Signed-off-by: dependabot[bot] <support@github.com>
2021-02-23 05:51:48 +00:00
dependabot[bot] e621a7937a
chore(deps): bump actions/cache from v2 to v2.1.4
Bumps [actions/cache](https://github.com/actions/cache) from v2 to v2.1.4.
- [Release notes](https://github.com/actions/cache/releases)
- [Commits](https://github.com/actions/cache/compare/v2...26968a09c0ea4f3e233fdddbafd1166051a095f6)

Signed-off-by: dependabot[bot] <support@github.com>
2021-02-08 06:31:24 +00:00
MDLeom ef57b75638
docs(precompressed): link to caddy guide 2020-11-13 23:04:16 +00:00
MDLeom 5cc13c1ad8 docs(br): GitLab Pages now support pre-compressed br
- https://gitlab.com/gitlab-org/gitlab-pages/-/merge_requests/359
2020-11-12 02:05:51 +00:00
MDLeom 140a005dc7 docs(br): Caddy v2 doesn't server pre-compressed files automatically 2020-11-12 02:05:51 +00:00
MDLeom ea29aa0ad5
Merge pull request #33 from curbengh/node-15
chore(actions): add Node 15
2020-11-07 14:18:47 +10:30
MDLeom bbc464b29e
chore(actions): simpler Node version syntax 2020-11-07 03:43:57 +00:00
MDLeom cef2d0e377
chore(actions): add Node 15 2020-11-07 03:42:45 +00:00
MDLeom 91c4d936e0
Merge pull request #32 from curbengh/dependabot/npm_and_yarn/standard-16.0.1
chore(deps-dev): bump standard from 14.3.4 to 16.0.1
2020-11-07 11:45:40 +10:30
MDLeom 4a16fca23e
Merge pull request #27 from curbengh/dependabot/github_actions/actions/setup-node-v2.1.2
chore(dev-deps): bump actions/setup-node from v2.1.1 to v2.1.2
2020-11-07 11:43:57 +10:30
dependabot[bot] d8805d9a9a
chore(deps-dev): bump standard from 14.3.4 to 16.0.1
Bumps [standard](https://github.com/standard/standard) from 14.3.4 to 16.0.1.
- [Release notes](https://github.com/standard/standard/releases)
- [Changelog](https://github.com/standard/standard/blob/master/CHANGELOG.md)
- [Commits](https://github.com/standard/standard/compare/v14.3.4...v16.0.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-11-02 07:11:23 +00:00
dependabot[bot] 19424d8ae8
chore(deps): bump actions/setup-node from v2.1.1 to v2.1.2
Bumps [actions/setup-node](https://github.com/actions/setup-node) from v2.1.1 to v2.1.2.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v2.1.1...c6fd00ceb9747fb23ffdf72987450a2664414867)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-02 06:11:47 +00:00
MDLeom 18e1fd3a78
ci(snyk): requires deps to be installed beforehand 2020-09-09 05:23:11 +00:00
MDLeom bc742f75fb
Merge pull request #26 from curbengh/dependabot/github_actions/actions/setup-node-v2.1.1
chore(deps): bump actions/setup-node from v1 to v2.1.1
2020-09-06 10:07:56 +09:30
dependabot[bot] 11b07ab00f
chore(deps): bump actions/setup-node from v1 to v2.1.1
Bumps [actions/setup-node](https://github.com/actions/setup-node) from v1 to v2.1.1.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v1...321b6ccb03083caa2ad22b27dc4b45335212e824)

Signed-off-by: dependabot[bot] <support@github.com>
2020-09-06 00:05:01 +00:00
MDLeom 0fd2ed31ee
release: 4.2.0 2020-09-06 00:04:28 +00:00
MDLeom ba66c4bc89
chore(dependabot): monitor actions' dep
- https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates#package-ecosystem
2020-09-06 00:02:26 +00:00
MDLeom 9ac8969a08
docs: Netlify supports brotli 2020-09-05 14:15:58 +00:00
MDLeom 353ca8ea37
docs: links to xml & json sections 2020-09-05 14:05:10 +00:00
MDLeom fd117bcd58
docs(badge): add codecov 2020-09-05 13:51:51 +00:00
MDLeom c8cc870b4c
docs(badge): add logo 2020-09-05 13:49:02 +00:00
MDLeom 327b36782b
ci(snyk): fix cron syntax
- docs: add actions badge
2020-09-05 12:47:23 +00:00
MDLeom 57db21d2ad
ci: replace travis with actions 2020-09-05 12:43:05 +00:00
MDLeom 51cc9b1d8d
feat: show path that caused the error 2020-09-05 11:58:20 +00:00
MDLeom c57e227efd
fix: set default config 2020-09-05 11:41:22 +00:00
MDLeom 37ba1d9a2b
refactor: destructure 2020-09-05 11:22:36 +00:00
MDLeom ce8c4a145b
refactor(test): set default config in individual test 2020-09-05 11:18:35 +00:00
MDLeom 16cdf467d9
fix(js): compatibility with terser v5
- https://github.com/terser/terser/blob/master/CHANGELOG.md#v500-beta0
2020-09-05 10:56:33 +00:00
MDLeom 3de777904f
chore(deps-dev): update hexo from 25.4.0 to 26.4.2 2020-09-05 10:23:32 +00:00
MDLeom d9817f8a44
chore(deps-dev): update hexo from 4.2.0 to 5.1.1 2020-09-05 10:23:01 +00:00
MDLeom 605c5fd57f
chore(deps): update terser from 4.0.0 to 5.3.0 2020-09-05 10:21:22 +00:00
MDLeom d68c26a2c2
docs: use Shields.io badges
- move david-dm.org to more stable libraries.io
2020-09-05 10:18:51 +00:00
MDLeom 7f60c6959e
chore: replace deprecated greenkeeper with dependabot 2020-09-05 10:10:34 +00:00
MDLeom c2bcaa7d69
Merge pull request #25 from curbengh/minify-xml
feat: utilise better minify-xml
2020-09-03 14:37:16 +09:30
MDLeom 8bbecf676f
feat: utilise better minify-xml
- replace pretty-data
2020-09-02 11:23:20 +00:00
MDLeom 9b94a2c2df
ci(travis): remove Node 13
- EOL 1 June 2020
2020-06-06 07:45:47 +01:00
MDLeom bdb7e94350
test(gz): blns 2020-06-06 07:42:12 +01:00
MDLeom e5ab822c45
test(br): blns 2020-06-06 07:41:37 +01:00
MDLeom c42b9040a6
test(js): throw error on invalid input 2020-05-25 09:36:21 +01:00
MDLeom 9e67d11e8a
test(html): invalid input
- uncovered using blns.json
- https://github.com/minimaxir/big-list-of-naughty-strings
2020-05-25 09:13:15 +01:00
MDLeom e1843f7400
chore(deps-dev): Update jest to 25.4.0 2020-04-24 13:57:26 +01:00
MDLeom b8d48c7f8a
ci(travis): add Node 14 2020-04-24 13:54:24 +01:00
MDLeom c721fa8c02
docs(readme): add lgtm.com badges 2020-02-24 04:11:50 +00:00
curbengh 81ab472fe1
test: utilise toThrow() to catch error
- try...catch would (incorrectly) pass the test, despite having no error
2020-01-09 15:14:25 +00:00
curbengh b9f4cd6173
release: v4.1.0 2020-01-03 01:37:33 +00:00
curbengh fdcdbef4a1
feat: minify json 2020-01-03 01:17:37 +00:00
curbengh 64ba16cb22
refactor(test): organise unit tests into separate files 2020-01-03 00:51:45 +00:00
curbengh 317b5e43d3
feat: minify xml 2020-01-03 00:35:25 +00:00
curbengh d75b10b64b
fix: skip empty file 2020-01-02 12:49:29 +00:00
curbengh 8d0fdaeb80
docs: clarify logger -> verbose 2020-01-02 10:46:28 +00:00
curbengh ab69db2f3e
release: v4.0.0 2020-01-02 00:47:55 +00:00
curbengh d1ca7f9c2b
refactor(test): remove unnecessary variable 2019-12-30 06:01:28 +00:00
curbengh 4a5c66add2
style(test): it() to test() 2019-12-30 05:53:28 +00:00
curbengh fa6f514a05
ci: add code coverage report 2019-12-29 04:14:49 +00:00
curbengh 60642c8048
docs: add TOC 2019-12-29 03:51:02 +00:00
curbengh 1c530b7d44
test(include): negation pattern and basename 2019-12-29 02:33:59 +00:00
curbengh 51ff9d953d
fix(test): corrects slash pattern tests 2019-12-28 10:26:25 +00:00
curbengh 50d80ccc70
feat(globbing): support disabling basename for each pattern in 'include:' option 2019-12-28 10:10:30 +00:00
curbengh 9d6b72422b
refactor(test): utilize Buffer.equals
- instead of binary-base64 conversion
- https://jestjs.io/docs/en/expect#toequalvalue
2019-12-28 08:42:14 +00:00
curbengh 6111d21a7c
docs: 'logger:', compression level, 'basename' globOptions 2019-12-28 01:12:40 +00:00
curbengh 7201b3f56f
refactor(test): de-duplicate variables 2019-12-27 23:12:46 +00:00
curbengh 440b9ccd8c
feat: rename 'logger:' option to 'verbose:'
BREAKING CHANGE
2019-12-27 22:54:09 +00:00
curbengh e59e3866b9
test(br): invalid level 2019-12-27 09:12:27 +00:00
curbengh 1a5197b9c9
test(logger): add test 2019-12-27 08:55:39 +00:00
curbengh 866593110e
test(br): test passing option 2019-12-27 08:09:31 +00:00
curbengh f0b37aaf45
chore: requires at least Node 10.18
BREAKING CHANGE: Drop Node 8 and iltorb
- utilize native brotli
- Drop string-to-buffer conversion
  * conversion was necessary for iltorb
2019-12-27 08:00:16 +00:00
curbengh 48868bfa7e
feat(gz & br): support custom compression level 2019-12-27 07:50:03 +00:00
curbengh ab023da82e
test(brotli): add unit test 2019-12-27 05:59:44 +00:00
curbengh 626369e3e0
test(gzip): add unit test 2019-12-27 05:39:29 +00:00
curbengh 7e1ec95f9a
test(svg): add unit test 2019-12-27 04:32:03 +00:00
curbengh 932d36182c
fix: reject a promise with an error object
- resolve "Unhandled promise rejection" error
- https://stackoverflow.com/a/47803880
2019-12-27 04:30:10 +00:00
curbengh 9df4e8283b
fix(logger): pass the correct variable to the parameter 2019-12-27 03:31:33 +00:00
curbengh 7d163bcb00
test(js): result should not be undefined 2019-12-27 00:38:44 +00:00
curbengh 4e4605e1a4
test(css): invalid option 2019-12-26 15:32:21 +00:00
curbengh f7c600545b
test: option.enable 2019-12-26 15:24:54 +00:00
curbengh 631503aa1a
fix: Terser should throw error if there is invalid option
- Other functions also throw error to abort
2019-12-26 15:06:32 +00:00
curbengh b046d2a09e
fix: disable basename by invidual pattern 2019-12-26 14:44:42 +00:00
curbengh e6381ebed3
Merge pull request #21 from curbengh/unit-test
test: add tests for html, js, css minifiers
2019-12-27 00:22:09 +10:30
curbengh e9609de460
ci: avoid running snyk in PR 2019-12-26 13:32:14 +00:00
curbengh 679dcaf077
test: add tests for html, js, css minifiers 2019-12-26 13:19:39 +00:00
curbengh 9263366537
ci: Node 10, 12 & 13 2019-12-26 11:14:53 +00:00
curbengh 4dc883d269
ci: drop deprecated sudo option 2019-12-26 11:13:59 +00:00
curbengh 0c270a4594
release: v3.2.3 2019-12-16 08:45:28 +00:00
curbengh 7eda55a1aa perf: use + operator
instead of Array.push()
https://stackoverflow.com/a/55409094
2019-12-16 08:43:35 +00:00
curbengh 1fdac53793 style: object-shorthand
https://eslint.org/docs/rules/object-shorthand
2019-12-16 08:43:35 +00:00
curbengh 89a6dcea9e
release: v3.2.2 2019-11-09 07:26:42 +00:00
curbengh 2548eb20bf
fix(js): remove 'priority' option
- Terser really doesn't like unsupported options
- 'priority' is introduced in 887eeda3cc
2019-11-09 07:18:59 +00:00
curbengh de425e4b6f
refactor: destructure object 2019-11-09 07:16:07 +00:00
curbengh 85bfd3ba35
release: v3.2.1 2019-10-10 08:06:54 +01:00
curbengh 213700a617
fix: array should join() without a separator
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join
2019-10-10 08:04:56 +01:00
curbengh 26b466c1bf
release: v3.2.0 2019-10-09 23:34:32 +01:00
curbengh 887eeda3cc
feat: add 'priority' options 2019-10-09 23:33:34 +01:00
curbengh 48bf9951f0
fix: join Array, instead of using first index 2019-10-06 09:24:25 +01:00
curbengh 461fe1dd41
fix: bind 'this' to error() 2019-10-06 06:33:35 +01:00
curbengh 5de4a237b7
refactor: convert Stream to Buffer using Array.push() 2019-10-06 06:32:35 +01:00
curbengh 2c5ad19932
fix: utilize hexo.log to display error message 2019-10-04 08:27:25 +01:00
curbengh 680ee73a03
release: v3.1.0 2019-09-25 08:44:30 +01:00
curbengh c2e6b898a9
Merge pull request #18 from curbengh/native-brotli
feat: support native brotli
2019-09-27 17:31:30 +09:30
curbengh e473408673
feat: support native brotli 2019-09-25 01:33:47 +01:00
curbengh 60a045e429
refactor: async/await
- https://dev.to/gafi/7-reasons-to-always-use-async-await-over-plain-promises-tutorial-4ej9
- https://stackoverflow.com/a/56975197
2019-09-25 01:14:37 +01:00
curbengh 90bfe2ca36
style: to rename 'logger' to 'verbose' 2019-09-24 23:56:44 +01:00
curbengh fd474580ce
refactor: destructure object 2019-09-24 23:55:07 +01:00
curbengh 7085b15d10
style: rename logic* to minify* or *Fn 2019-09-15 16:55:36 +01:00
curbengh b5639aba85
refactor: logger function 2019-09-15 16:51:15 +01:00
curbengh 36bca1b939
refactor: use const function 2019-09-15 16:16:53 +01:00
curbengh 9cb977e2d2
Merge pull request #17 from curbengh/options
feat: enable plugin by default and update options naming
2019-09-13 11:29:36 +09:30
curbengh ff3b07c9e2
release: v3.0.0 2019-09-11 02:48:40 +01:00
curbengh ddf4415ef0
docs: update options naming 2019-09-11 02:48:32 +01:00
curbengh 7e85bd6338
feat: enable plugin by default and update options naming 2019-09-11 02:15:19 +01:00
curbengh 2930f1da1d
release: v2.2.1 2019-08-09 11:49:15 +09:30
curbengh ede71a0857
fix: options.exclude might not exist
Fixes a6f59a9704 (r34629621)
2019-08-09 11:48:08 +09:30
curbengh b054c0a24b
docs(readme): update globOptions 2019-08-08 09:08:51 +09:30
curbengh 5b8549077f
release: v2.2.0 2019-08-06 12:06:00 +09:30
curbengh d964918d21
chore: use simpler repo value 2019-08-06 12:05:16 +09:30
curbengh 79308b2d44
Merge pull request #16 from curbengh/lint
test: lint using Javascript Standard Style
2019-08-06 12:02:41 +09:30
curbengh 32a519928a
style: standard 2019-08-06 11:57:44 +09:30
curbengh d72bb5b567 test: lint using Javascript Standard Style
https://standardjs.com/
2019-08-06 11:55:45 +09:30
curbengh 2725e230e3 chore(travis): use bionic and disable sudo 2019-08-06 11:55:45 +09:30
curbengh bbba062411 chore(travis): use newer cache syntax 2019-08-06 11:55:45 +09:30
curbengh 493683644a chore(travis): remove comment and npm install 2019-08-06 11:55:45 +09:30
curbengh 4923f5c39d
Merge pull request #15 from curbengh/basename
feat: disable basename globbing when pattern contains slash
2019-08-06 11:36:11 +09:30
curbengh a6f59a9704
feat: disable basename globbing when pattern contains slash 2019-08-06 11:26:11 +09:30
curbengh 43a316d149
refactor: use basename instead of matchBase 2019-08-06 11:23:58 +09:30
curbengh 7d06de7e79 release: v2.1.1 2019-07-18 17:34:54 +09:30
curbengh 37dac8c011 chore: update username 2019-07-18 17:34:26 +09:30
weyusi 374b24e9f4 release: v2.1.0 2019-07-10 12:17:00 +09:30
weyusi 41ecf75f71 chore: npmignore is not used 2019-07-10 12:16:01 +09:30
weyusi 5fac46455c test(snyk): remove nanomatch and add lodash 2019-07-10 12:14:21 +09:30
weyusi a75fc79e3d docs: tidy up readme 2019-07-10 12:04:37 +09:30
weyusi ba77db0094
Merge pull request #14 from weyusi/micromatch
feat: switch nanomatch to micromatch
2019-07-10 11:59:38 +09:30
weyusi c69b6d3356 feat: switch nanomatch to micromatch 2019-07-10 11:50:23 +09:30
weyusi 55908e655a chore: ignore optional deps 2019-06-22 14:22:08 +09:30
weyusi 98f174cd7a chore: specify minor version of node
* https://node.green
2019-06-22 14:16:16 +09:30
weyusi feaf654f15 test(snyk): add set-value and mixin-deep
* https://snyk.io/vuln/SNYK-JS-MIXINDEEP-450212
* https://snyk.io/vuln/SNYK-JS-SETVALUE-450213
* Remove braces and chownr
2019-06-22 14:10:58 +09:30
weyusi 7203ea2abe release: v2.0.4 2019-06-08 17:25:53 +09:30
weyusi 0ce066c2fd fix(html-minifier): ignore post excerpt tag
https://hexo.io/docs/tag-plugins#Post-Excerpt
2019-06-08 17:22:40 +09:30
weyusi 655f65adcd refactor: add 'use strict' 2019-05-27 10:44:04 +09:30
weyusi 654d279378
Merge pull request #12 from weyusi/terser-v4
chore(deps): Update terser from 3.17.0 to 4.0.0
2019-05-21 17:00:07 +09:30
weyusi 0623ca7e31 chore(deps): Update terser from 3.17.0 to 4.0.0 2019-05-21 16:56:01 +09:30
weyusi 3929884c8c chore(package): Set PR title for greenkeeper
https://greenkeeper.io/docs.html#custom-pr-titles
2019-05-21 16:46:47 +09:30
weyusi 587b460029 chore(package): use dependabot commit message
* (package) for non-deps change
2019-05-21 16:36:43 +09:30
weyusi 9c8af3b30f chore(package): change greenkeeper commit message
ref:
https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#type
https://github.com/hexojs/hexo/commits/master
2019-05-21 16:31:57 +09:30
weyusi 9bb184e9cf release: v2.0.3 2019-05-21 13:47:14 +09:30
weyusi db58c8e686 fix: typo in default config of svgo 2019-05-21 13:45:18 +09:30
weyusi 8531b77cfc style: arrow function
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
2019-05-21 11:16:14 +09:30
26 changed files with 3150 additions and 378 deletions

10
.github/dependabot.yml vendored Normal file
View File

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

22
.github/workflows/linter.yml vendored Normal file
View File

@ -0,0 +1,22 @@
name: Linter
on: [push, pull_request]
jobs:
linter:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Bun
uses: oven-sh/setup-bun@v1
- 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

23
.github/workflows/semgrep.yml vendored Normal file
View File

@ -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

37
.github/workflows/snyk.yml vendored Normal file
View File

@ -0,0 +1,37 @@
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@v4
- name: Install Bun
uses: oven-sh/setup-bun@v1
- 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

76
.github/workflows/tester.yml vendored Normal file
View File

@ -0,0 +1,76 @@
name: Tester
on: [push, pull_request]
jobs:
tester:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: ["18", "20", "22"]
fail-fast: false
steps:
- uses: actions/checkout@v4
- 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: Install Dependencies
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
run: npm run test
env:
CI: true
coverage:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
node-version: ["20.x"]
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Install Bun
uses: oven-sh/setup-bun@v1
- 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: Coverage
run: npm run test
env:
CI: true
- name: Upload coverage report to Codecov
uses: codecov/codecov-action@v4
with:
fail_ci_if_error: true
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

2
.gitignore vendored
View File

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

View File

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

1
.npmrc
View File

@ -1 +1,2 @@
package-lock=false
optional=false

13
.snyk
View File

@ -2,15 +2,4 @@
version: v1.12.0
# ignores vulnerabilities until expiry date; change duration by modifying expiry date
ignore:
'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: {}
patch:

View File

@ -1,20 +0,0 @@
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

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2018-2019 weyusi, 2016-2018 rozbo
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

371
README.md
View File

@ -1,168 +1,319 @@
# Hexo-yam
# hexo-yam
[![npm version](https://badge.fury.io/js/hexo-yam.svg)](https://www.npmjs.com/package/hexo-yam)
[![Build Status](https://travis-ci.com/weyusi/hexo-yam.svg?branch=master)](https://travis-ci.com/weyusi/hexo-yam)
[![NPM Dependencies](https://david-dm.org/weyusi/hexo-yam.svg)](https://david-dm.org/weyusi/hexo-yam)
[![Known Vulnerabilities](https://snyk.io/test/npm/hexo-yam/badge.svg)](https://snyk.io/test/npm/hexo-yam)
[![Greenkeeper badge](https://badges.greenkeeper.io/weyusi/hexo-yam.svg)](https://greenkeeper.io/)
[![npm version](https://img.shields.io/npm/v/hexo-yam?logo=npm)](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)
[![codecov](https://img.shields.io/codecov/c/gh/curbengh/hexo-yam?logo=codecov)](https://codecov.io/gh/curbengh/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/github/curbengh/hexo-yam/badge.svg)](https://snyk.io/test/github/curbengh/hexo-yam)
> This project is based on [hexo-neat](https://github.com/rozbo/hexo-neat)
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.
Yet Another Minifier for Hexo. Minify and compress html, js, css and svg. 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).
## Table of contents
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.
All the options are the same, so you can use this as a drop-in replacement.
*Note:* See [HTTP Compression](#http-compression) section below for more info on using brotli.
- [Installation](#installation)
- [Options](#options)
- [HTML](#html)
- [CSS](#css)
- [JS](#js)
- [SVG](#svg)
- [XML](#xml)
- [JSON](#json)
- [Gzip](#gzip)
- [Brotli](#brotli)
- [Zstd](#zstd)
- [Globbing](#globbing)
## Installation
``` bash
$ npm install hexo-yam --save
```
## Usage
To enable this plugin, insert the following to `_config.yml`:
``` yaml
neat_enable: true
```bash
$ npm install --save hexo-yam
```
For further customization, see below.
## Options
``` yaml
neat_html:
```yaml
minify:
enable: true
exclude:
previewServer: true
html:
css:
js:
svg:
gzip:
brotli:
xml:
json:
```
- **enable** - Enable the plugin. Defaults to `true`.
- **logger** - Verbose output. Defaults to `false`.
- **exclude** - Exclude files. Support [wildcard](https://github.com/micromatch/nanomatch#features) glob pattern.
- Support one-liner, `exclude: [*.min.html, *.note.html]`.
- To exclude a file, double asterisk and the full path must be specified, `**/themes/typing/source/js/source.js`.
- `*source.js` also works, but it also excludes `resource.js`.
- Test glob pattern on the web using [Globtester](http://www.globtester.com/).
- **previewServer** - Disable the plugin when running `hexo server`. Defaults to `true`.
- **html** - See [HTML](#html) section
- **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).
----------
## CSS
``` yaml
neat_css:
enable: true
exclude:
- '*.min.css'
```yaml
minify:
css:
enable: true
exclude:
- "*.min.css"
```
- **enable** - Enable the plugin. Defaults to `true`.
- **logger** - Verbose output. Defaults to `false`.
- **exclude** - Exclude files. Support wildcard pattern.
- **priority** - Plugin's priority. Defaults to `10`.
- **verbose** - Verbose output. Defaults to `false`.
- **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:
enable: true
exclude:
- '*.min.js'
```yaml
minify:
js:
enable: true
exclude:
- "*.min.js"
```
- **enable** - Enable the plugin. Defaults to `true`.
- **logger** - Verbose output. Defaults to `false`.
- **exclude** - Exclude files. Support wildcard pattern.
- **priority** - Plugin's priority. Defaults to `10`.
- **verbose** - Verbose output. Defaults to `false`.
- **exclude** - Exclude files. Support [wildcard](http://www.globtester.com/) pattern(s) in a string or array.
- **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).
- **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).
----------
## SVG
``` yaml
neat_svg:
enable: true
include:
- '*.svg'
- '!*.min.svg'
```yaml
minify:
svg:
enable: true
include:
- "*.svg"
- "!*.min.svg"
```
- **enable** - Enable the plugin. Defaults to `true`.
- **logger** - Verbose output. Defaults to `false`.
- **include** - Include files. Support wildcard pattern.
- **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.svg` by default.
- **plugin** - Plugin options.
- To retain comments, `plugins: [{removeComments: false}]`.
- **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.
For more options, see [svgo](https://github.com/svg/svgo).
## XML
----------
Remove whitespaces in xml.
``` yaml
neat_gzip:
enable: true
include:
- '*.html'
- '*.css'
- '*.js'
- '*.txt'
- '*.ttf'
- '*.atom'
- '*.stl'
- '*.xml'
- '*.svg'
- '*.eot'
- '*.json'
```yaml
minify:
xml:
enable: false
include:
- "*.xml"
- "!*.min.xml"
```
- **enable** - Enable the plugin. Defaults to `false`.
- **priority** - Plugin's priority. Defaults to `10`.
- **verbose** - Verbose output. Defaults to `false`.
- **include** - Include files. Support [wildcard](http://www.globtester.com/) pattern(s) in a string or array.
- Exclude `*.min.xml` by default.
- **removeComments** - Remove [comments](https://developer.mozilla.org/en-US/docs/Web/XML/XML_introduction) in xml. Defaults to `true`.
- **globOptions** - See [globbing](#globbing) section.
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`.
- **logger** - Verbose output. Defaults to `false`.
- **include** - Include files. Support wildcard pattern.
- **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
neat_brotli:
enable: true
include:
- '*.html'
- '*.css'
- '*.js'
- '*.txt'
- '*.ttf'
- '*.atom'
- '*.stl'
- '*.xml'
- '*.svg'
- '*.eot'
- '*.json'
```yaml
minify:
brotli:
enable: true
include:
- "*.html"
- "*.css"
- "*.js"
- "*.txt"
- "*.ttf"
- "*.atom"
- "*.stl"
- "*.xml"
- "*.svg"
- "*.eot"
- "*.json"
```
- **enable** - Enable the plugin. Defaults to `true`.
- **logger** - Verbose output. Defaults to `false`.
- **include** - Include files. Support wildcard pattern.
- **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)
## 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.
## Zstd
As of May 2019, GitHub Pages, GitLab Pages and Netlify *do not* support brotli yet. You can generate `.br` files, but they won't serve those files.
```yaml
minify:
zstd:
enable: false
include:
- "*.html"
- "*.css"
- "*.js"
- "*.txt"
- "*.ttf"
- "*.atom"
- "*.stl"
- "*.xml"
- "*.svg"
- "*.eot"
- "*.json"
```
Name | Status (May 8, 2019)
--- | ---
GitHub Pages | [In consideration](https://github.community/t5/GitHub-Pages/Support-for-pre-compressed-assets-and-brotli-compression/m-p/22055)
GitLab Pages | [In progress](https://gitlab.com/gitlab-org/gitlab-pages/merge_requests/120)
Netlify | [In consideration](https://postimg.cc/qgxn0261)
- **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)
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),
### Cannot find module '@mongodb-js/zstd-linux-x64-gnu'
- [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.
- [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.
`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
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
All credits go to the following work:
- [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)

5
bunfig.toml Normal file
View File

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

155
index.js
View File

@ -1,65 +1,108 @@
/* global hexo */
if (hexo.config.neat_enable === true) {
// HTML minifier
hexo.config.neat_html = Object.assign({
enable: true,
logger: false,
exclude: [],
collapseBooleanAttributes: true,
collapseWhitespace: true,
removeComments: true,
removeEmptyAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
minifyJS: true,
minifyCSS: true
}, hexo.config.neat_html)
'use strict'
// CSS minifier
hexo.config.neat_css = Object.assign({
enable: true,
logger: false,
exclude: ['*.min.css'],
level: 2
}, hexo.config.neat_css)
hexo.config.minify = Object.assign({
enable: true,
previewServer: true
}, hexo.config.minify)
// Javascript minifier
hexo.config.neat_js = Object.assign({
enable: true,
logger: false,
exclude: ['*.min.js'],
compress: {},
mangle: true,
output: {}
}, hexo.config.neat_js)
hexo.config.minify.html = Object.assign({
enable: true,
priority: 10,
verbose: false,
exclude: [],
collapseBooleanAttributes: true,
collapseWhitespace: true,
// Ignore '<!-- more -->' https://hexo.io/docs/tag-plugins#Post-Excerpt
ignoreCustomComments: [/^\s*more/],
removeComments: true,
removeEmptyAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
minifyJS: true,
minifyCSS: true,
globOptions: { basename: true }
}, hexo.config.minify.html)
// SVG minifier
hexo.config.neat_svg = Object.assign({
enable: true,
logger: false,
include: ['*.svg', '!*.min.svg'],
plugin: []
}, hexo.config.neat_svg)
hexo.config.minify.css = Object.assign({
enable: true,
priority: 10,
verbose: false,
exclude: ['*.min.css'],
level: 2,
globOptions: { basename: true }
}, hexo.config.minify.css)
// gzip compression
hexo.config.neat_gzip = Object.assign({
enable: true,
logger: false,
include: ['*.html', '*.css', '*.js', '*.txt', '*.ttf', '*.atom', '*.stl', '*.xml', '*.svg', '*.eot', '*.json']
}, hexo.config.neat_gzip)
hexo.config.minify.js = Object.assign({
enable: true,
priority: 10,
verbose: false,
exclude: ['*.min.js'],
compress: {},
mangle: true,
output: {},
globOptions: { basename: true }
}, hexo.config.minify.js)
// brotli compression
hexo.config.neat_brotli = Object.assign({
enable: true,
logger: false,
include: ['*.html', '*.css', '*.js', '*.txt', '*.ttf', '*.atom', '*.stl', '*.xml', '*.svg', '*.eot', '*.json']
}, hexo.config.neat_brotli)
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')
hexo.extend.filter.register('after_render:html', filter.logicHtml)
hexo.extend.filter.register('after_render:css', filter.logicCss)
hexo.extend.filter.register('after_render:js', filter.logicJs)
hexo.extend.filter.register('after_generate', filter.logicSvg)
hexo.extend.filter.register('after_generate', filter.logicGzip)
hexo.extend.filter.register('after_generate', filter.logicBrotli)
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:js', filter.minifyJs, hexo.config.minify.js.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.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,218 +1,337 @@
/* global hexo */
'use strict'
const Htmlminifier = require('html-minifier').minify
const { minify: htmlMinify } = require('html-minifier-terser')
const CleanCSS = require('clean-css')
const Terser = require('terser')
const svgo = require('svgo')
const { minify: terserMinify } = require('terser')
const { optimize: svgOptimize } = require('svgo')
const zlib = require('zlib')
const br = require('iltorb')
const nanomatch = require('nanomatch')
const { promisify } = require('util')
const gzip = promisify(zlib.gzip)
const br = promisify(zlib.brotliCompress)
const { minify: compressXml } = require('minify-xml')
const micromatch = require('micromatch')
const { compress: zstd } = require('@mongodb-js/zstd')
function logicHtml (str, data) {
const hexo = this
const options = hexo.config.neat_html
// Return if disabled.
if (options.enable === false) return
let path = data.path
let exclude = options.exclude
// Return if a path matches exclusion pattern
if (path && exclude && exclude.length) {
if (nanomatch.some(path, exclude, { matchBase: true })) return str
}
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
}
function logicCss (str, data) {
const hexo = this
const options = hexo.config.neat_css
// Return if disabled.
if (options.enable === false) return
let path = data.path
let exclude = options.exclude
// Return if a path matches exclusion pattern
if (path && exclude && exclude.length) {
if (nanomatch.some(path, exclude, { matchBase: true })) return str
}
return new Promise(function (resolve, reject) {
new CleanCSS(options).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 + '%')
const isMatch = (path = '', patterns = [], options = {}) => {
if (path && patterns) {
if (path.length && patterns.length) {
if (typeof patterns === 'string') patterns = [patterns]
for (const pattern of patterns) {
// disable basename if a pattern includes a slash
let { basename } = options
// only disable when basename is enabled
basename = basename && !pattern.includes('/')
if (micromatch.isMatch(path, pattern, { ...options, basename })) {
return true
}
}
})
})
}
}
return false
}
function logicJs (str, data) {
const hexo = this
const options = hexo.config.neat_js
// Return if disabled.
if (options.enable === false) return
const match = (paths = [], patterns = [], options = {}) => {
let input = paths
if (paths && patterns) {
if (paths.length && patterns.length) {
const output = []
if (typeof patterns === 'string') patterns = [patterns]
const exclude = patterns.filter((pattern) => pattern.startsWith('!'))
const include = patterns.filter((pattern) => !pattern.startsWith('!'))
if (exclude.length) input = micromatch(paths, exclude, options)
if (include.length) {
for (const pattern of include) {
let { basename } = options
basename = basename && !pattern.includes('/')
const tmp = micromatch(input, pattern, { ...options, basename })
if (tmp.length) output.push(...tmp)
}
return [...new Set(output)]
}
return input
}
}
return paths
}
let path = data.path
let exclude = options.exclude
function logFn (original, minified, path, ext) {
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 options = hexo.config.minify.html
if (options.enable === false || !str) return
const { path } = data
const { exclude, globOptions, verbose } = options
// Return if a path matches exclusion pattern
if (path && exclude && exclude.length) {
if (nanomatch.some(path, exclude, { matchBase: true })) return str
if (isMatch(path, exclude, globOptions)) 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
const jsOptions = Object.assign({}, options)
delete jsOptions.enable
delete jsOptions.exclude
delete jsOptions.priority
delete jsOptions.verbose
// Old option, retained to avoid crash when upgrading to v4
delete jsOptions.logger
delete jsOptions.exclude
delete jsOptions.globOptions
let result = Terser.minify(str, jsOptions)
let saved = ((str.length - result.code.length) / str.length * 100).toFixed(2)
if (options.logger) {
let log = hexo.log || console.log
log.log('Minify the js: %s [%s saved]', path, saved + '%')
try {
const { code } = await terserMinify(str, jsOptions)
if (verbose) logFn.call(this, str, code, path, 'js')
return code
} catch (err) {
throw new Error(`Path: ${path}\n${err}`)
}
return result.code
}
function logicSvg () {
function minifySvg () {
const hexo = this
const options = hexo.config.neat_svg
// Return if disabled.
const options = hexo.config.minify.svg
if (options.enable === false) return
let route = hexo.route
let routeList = route.list()
let include = options.include
const { route } = hexo
const 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((nanomatch(routeList, include, { matchBase: true })).map(path => {
return Promise.all((match(routeList, include, globOptions)).map((path) => {
return new Promise((resolve, reject) => {
// Grab all assets using hexo router
let assetPath = route.get(path)
const assetPath = route.get(path)
let assetTxt = ''
// Extract the content
assetPath.on('data', (chunk) => (assetTxt += chunk))
assetPath.on('end', () => {
assetPath.on('end', async () => {
if (assetTxt.length) {
// Minify using svgo
new svgo(options).optimize(assetTxt).then(function (result) {
// Replace the original file with the minified.
route.set(path, result.data)
// Logging
let saved = ((assetTxt.length - result.data.length) / assetTxt.length * 100).toFixed(2)
if (options.logger) {
let log = hexo.log || console.log
log.log('Minify the svg: %s [%s saved]', path, saved + '%')
}
resolve(assetTxt)
})
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 logicGzip () {
function gzipFn () {
const hexo = this
const options = hexo.config.neat_gzip
// Return if disabled.
const options = hexo.config.minify.gzip
if (options.enable === false) return
let route = hexo.route
let routeList = route.list()
let include = options.include
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((nanomatch(routeList, include, { matchBase: true })).map(path => {
return Promise.all((match(routeList, include, globOptions)).map((path) => {
return new Promise((resolve, reject) => {
// Grab all assets using hexo router
let assetPath = route.get(path)
const assetPath = route.get(path)
let assetTxt = ''
// Extract the content
assetPath.on('data', (chunk) => (assetTxt += chunk))
assetPath.on('end', () => {
assetPath.on('end', async () => {
if (assetTxt.length) {
// gzip compress using highest level
zlib.gzip(assetTxt, { level: zlib.constants.Z_BEST_COMPRESSION }, (err, Input) => {
if (!err) {
// Save the compressed file to .gz
route.set(path + '.gz', Input)
// 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)
}
})
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 logicBrotli () {
function brotliFn () {
const hexo = this
const options = hexo.config.neat_brotli
// Return if disabled.
const options = hexo.config.minify.brotli
if (options.enable === false) return
let route = hexo.route
let routeList = route.list()
let include = options.include
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((nanomatch(routeList, include, { matchBase: true })).map(path => {
return Promise.all((match(routeList, include, globOptions)).map((path) => {
return new Promise((resolve, reject) => {
// Grab all assets using hexo router
let assetPath = route.get(path)
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 = ''
// Extract the content
assetPath.on('data', (chunk) => (assetTxt += chunk))
assetPath.on('end', () => {
if (assetTxt.length) {
// Input has to be buffer for brotli
let input = Buffer.from(assetTxt, 'utf-8')
// brotli defaults to max compression level
br.compress(input, (err, output) => {
if (!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)
}
})
try {
const result = compressXml(assetTxt, { ...options })
if (verbose) logFn.call(this, assetTxt, result, path, 'xml')
resolve(route.set(path, result))
} catch (err) {
reject(new Error(`Path: ${path}\n${err}`))
}
}
resolve()
})
})
}))
}
function minifyJson () {
const hexo = this
const options = hexo.config.minify.json
if (options.enable === false) return
const { route } = hexo
const routeList = route.list()
const { globOptions, include, verbose } = options
return Promise.all((match(routeList, include, globOptions)).map((path) => {
return new Promise((resolve, reject) => {
const assetPath = route.get(path)
let assetTxt = ''
assetPath.on('data', (chunk) => (assetTxt += chunk))
assetPath.on('end', () => {
if (assetTxt.length) {
try {
const result = JSON.stringify(JSON.parse(assetTxt))
if (verbose) logFn.call(this, assetTxt, result, path, 'json')
resolve(route.set(path, result))
} catch (err) {
reject(new Error(`Path: ${path}\n${err}`))
}
}
resolve()
})
})
}))
}
module.exports = {
logicHtml: logicHtml,
logicCss: logicCss,
logicJs: logicJs,
logicSvg: logicSvg,
logicGzip: logicGzip,
logicBrotli: logicBrotli
minifyHtml,
minifyCss,
minifyJs,
minifySvg,
gzipFn,
brotliFn,
zstdFn,
minifyXml,
minifyJson
}

View File

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

258
test/brotli.test.js Normal file
View File

@ -0,0 +1,258 @@
/* eslint-env jest */
'use strict'
const Hexo = require('hexo')
const zlib = require('zlib')
const { promisify } = require('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)
})
}
})
})

107
test/css.test.js Normal file
View File

@ -0,0 +1,107 @@
/* 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)
})
})

516
test/fixtures/blns.json vendored Normal file
View File

@ -0,0 +1,516 @@
[
"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() }}"
]

260
test/gzip.test.js Normal file
View File

@ -0,0 +1,260 @@
/* eslint-env jest */
'use strict'
const Hexo = require('hexo')
const zlib = require('zlib')
const { promisify } = require('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)
})
}
})
})

132
test/html.test.js Normal file
View File

@ -0,0 +1,132 @@
/* 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>')
})
})

123
test/js.test.js Normal file
View File

@ -0,0 +1,123 @@
/* 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`)
})
})

190
test/json.test.js Normal file
View File

@ -0,0 +1,190 @@
/* 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)
})
})

253
test/svg.test.js Normal file
View File

@ -0,0 +1,253 @@
/* 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)
})
})

213
test/xml.test.js Normal file
View File

@ -0,0 +1,213 @@
/* 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)
})
})
})

256
test/zstd.test.js Normal file
View File

@ -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)
})
}
})
})