├── .eslintignore
├── .gitignore
├── .npmrc
├── .commitlintrc.json
├── packages
├── playground
│ └── basic
│ │ ├── .gitignore
│ │ ├── public
│ │ └── favicon.ico
│ │ ├── src
│ │ ├── assets
│ │ │ ├── test.gif
│ │ │ ├── test.jpg
│ │ │ ├── test.png
│ │ │ └── test.svg
│ │ ├── main.ts
│ │ └── App.vue
│ │ ├── shim-vue.ts
│ │ ├── index.html
│ │ ├── vite.config.ts
│ │ ├── tsconfig.json
│ │ └── package.json
└── core
│ ├── build.config.ts
│ ├── src
│ ├── types.ts
│ ├── utils.ts
│ └── index.ts
│ └── package.json
├── pnpm-workspace.yaml
├── vitest.config.ts
├── .prettierrc.json
├── .husky
├── commit-msg
├── common.sh
└── pre-commit
├── .vscode
├── extensions.json
└── settings.json
├── tsconfig.json
├── .eslintrc.json
├── .github
└── workflows
│ ├── release.yml
│ └── publish.yml
├── LICENSE
├── README.zh_CN.md
├── package.json
├── README.md
└── CHANGELOG.md
/.eslintignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 | es
5 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | ignore-workspace-root-check=true
2 | public-hoist-pattern[]=*
3 |
--------------------------------------------------------------------------------
/.commitlintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["@commitlint/config-conventional"]
3 | }
4 |
--------------------------------------------------------------------------------
/packages/playground/basic/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 | *.local
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - packages/*
3 | - packages/playground/*
4 |
--------------------------------------------------------------------------------
/vitest.config.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import { defineConfig } from 'vite'
4 |
5 | export default defineConfig({})
6 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "tabWidth": 2,
4 | "singleQuote": true,
5 | "printWidth": 80,
6 | "trailingComma": "all"
7 | }
8 |
--------------------------------------------------------------------------------
/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # shellcheck source=./_/husky.sh
4 | . "$(dirname "$0")/_/husky.sh"
5 |
6 | npx --no-install commitlint --edit "$1"
7 |
--------------------------------------------------------------------------------
/packages/playground/basic/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbenjs/vite-plugin-compression/HEAD/packages/playground/basic/public/favicon.ico
--------------------------------------------------------------------------------
/packages/playground/basic/src/assets/test.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbenjs/vite-plugin-compression/HEAD/packages/playground/basic/src/assets/test.gif
--------------------------------------------------------------------------------
/packages/playground/basic/src/assets/test.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbenjs/vite-plugin-compression/HEAD/packages/playground/basic/src/assets/test.jpg
--------------------------------------------------------------------------------
/packages/playground/basic/src/assets/test.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbenjs/vite-plugin-compression/HEAD/packages/playground/basic/src/assets/test.png
--------------------------------------------------------------------------------
/packages/playground/basic/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import App from './App.vue'
3 |
4 | const app = createApp(App)
5 |
6 | app.mount('#app')
7 |
--------------------------------------------------------------------------------
/packages/playground/basic/shim-vue.ts:
--------------------------------------------------------------------------------
1 | declare module '*.vue' {
2 | import { ComponentOptions } from 'vue'
3 | const component: ComponentOptions
4 | export default component
5 | }
6 |
--------------------------------------------------------------------------------
/.husky/common.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | command_exists () {
3 | command -v "$1" >/dev/null 2>&1
4 | }
5 |
6 | # Workaround for Windows 10, Git Bash and Yarn
7 | if command_exists winpty && test -t 1; then
8 | exec < /dev/tty
9 | fi
10 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 | . "$(dirname "$0")/common.sh"
4 |
5 | [ -n "$CI" ] && exit 0
6 |
7 | # Format and submit code according to lintstagedrc.js configuration
8 | pnpm exec lint-staged --concurrent false
9 |
--------------------------------------------------------------------------------
/packages/core/build.config.ts:
--------------------------------------------------------------------------------
1 | import { defineBuildConfig } from 'unbuild'
2 |
3 | export default defineBuildConfig({
4 | clean: true,
5 | entries: ['./src/index'],
6 | declaration: true,
7 | rollup: {
8 | emitCJS: true,
9 | },
10 | })
11 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "octref.vetur",
4 | "dbaeumer.vscode-eslint",
5 | "stylelint.vscode-stylelint",
6 | "esbenp.prettier-vscode",
7 | "mrmlnc.vscode-less",
8 | "lokalise.i18n-ally",
9 | "antfu.iconify",
10 | "mikestead.dotenv",
11 | "heybourn.headwind"
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/packages/playground/basic/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "ESNext",
4 | "target": "es2017",
5 | "moduleResolution": "node",
6 | "strict": true,
7 | "declaration": true,
8 | "noUnusedLocals": true,
9 | "esModuleInterop": true,
10 | "outDir": "dist",
11 | "lib": ["ESNext"],
12 | "sourceMap": false,
13 | "noEmitOnError": true,
14 | "noImplicitAny": false
15 | },
16 | "include": ["./packages"],
17 | "exclude": ["**/dist", "**/node_modules", "**/test"]
18 | }
19 |
--------------------------------------------------------------------------------
/packages/playground/basic/vite.config.ts:
--------------------------------------------------------------------------------
1 | import vue from '@vitejs/plugin-vue'
2 | import jsx from '@vitejs/plugin-vue-jsx'
3 | import viteCompression from 'vite-plugin-compression'
4 |
5 | export default () => {
6 | return {
7 | build: {
8 | assetsInlineLimit: 0,
9 | },
10 | plugins: [
11 | vue(),
12 | jsx(),
13 | // gizp
14 | viteCompression(),
15 | // br
16 | viteCompression({
17 | algorithm: 'brotliCompress',
18 | }),
19 | ],
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/packages/playground/basic/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
30 |
--------------------------------------------------------------------------------
/packages/playground/basic/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2016",
4 | "module": "esnext",
5 | "moduleResolution": "node",
6 | "strict": true,
7 | "forceConsistentCasingInFileNames": true,
8 | "allowSyntheticDefaultImports": true,
9 | "strictFunctionTypes": false,
10 | "jsx": "preserve",
11 | "baseUrl": ".",
12 | "allowJs": true,
13 | "sourceMap": true,
14 | "esModuleInterop": true,
15 | "noUnusedLocals": true,
16 | "noUnusedParameters": true,
17 | "experimentalDecorators": true,
18 | "lib": ["dom", "esnext"],
19 | "incremental": true,
20 | "skipLibCheck": true
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "env": {
4 | "browser": true,
5 | "es2021": true,
6 | "es6": true,
7 | "node": true
8 | },
9 | "extends": [
10 | "eslint:recommended",
11 | "plugin:@typescript-eslint/recommended",
12 | "prettier"
13 | ],
14 | "parser": "@typescript-eslint/parser",
15 | "parserOptions": {
16 | "ecmaVersion": 12,
17 | "sourceType": "module"
18 | },
19 | "plugins": ["@typescript-eslint"],
20 | "rules": {
21 | "no-useless-catch": "off",
22 | "no-console": 1,
23 | "@typescript-eslint/no-non-null-assertion": "off",
24 | "@typescript-eslint/ban-ts-comment": "off",
25 | "@typescript-eslint/no-explicit-any": "off"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/packages/playground/basic/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "playground-basic",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "vite",
7 | "build": "vite build ",
8 | "test:gzip": "npm run build && http-server dist --cors --gzip -c-1",
9 | "test:br": "npm run build && http-server dist --cors --brotli -c-1"
10 | },
11 | "dependencies": {
12 | "vue": "^3.2.29"
13 | },
14 | "devDependencies": {
15 | "@vitejs/plugin-vue": "^2.1.0",
16 | "@vitejs/plugin-vue-jsx": "^1.3.3",
17 | "@vue/compiler-sfc": "^3.2.29",
18 | "cross-env": "^7.0.3",
19 | "http-server": "^14.1.0",
20 | "typescript": "^4.5.5",
21 | "vite": "^2.7.13",
22 | "vite-plugin-compression": "workspace:*"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Create Release
2 |
3 | on:
4 | push:
5 | tags:
6 | - v*
7 |
8 | jobs:
9 | build:
10 | name: Create Release
11 | runs-on: ${{matrix.os}}
12 |
13 | strategy:
14 | matrix:
15 | os: [ubuntu-latest]
16 | fail-fast: false
17 |
18 | steps:
19 | - name: Checkout code
20 | uses: actions/checkout@master
21 |
22 | - name: Create Release for Tag
23 | id: release_tag
24 | uses: yyx990803/release-tag@master
25 | env:
26 | GITHUB_TOKEN: ${{ secrets.OPER_TOKEN }}
27 | with:
28 | tag_name: ${{ github.ref }}
29 | body: |
30 | Please refer to [CHANGELOG.md](https://github.com/anncwb/vite-plugin-compression/blob/main/CHANGELOG.md) for details.
31 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Npm Publish
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | publish-npm:
10 | if: "contains(github.event.head_commit.message, 'release')"
11 | runs-on: ${{matrix.os}}
12 |
13 | strategy:
14 | matrix:
15 | os: [ubuntu-latest]
16 | node-version: [16.x]
17 | fail-fast: false
18 |
19 | steps:
20 | - name: Checkout
21 | uses: actions/checkout@v2
22 |
23 | - name: Install pnpm
24 | uses: pnpm/action-setup@v2.0.1
25 | with:
26 | version: 6.28.0
27 |
28 | - name: Use Node.js ${{ matrix.node-version }}
29 | uses: actions/setup-node@v2
30 | with:
31 | node-version: ${{ matrix.node-version }}
32 | registry-url: https://registry.npmjs.org/
33 | cache: 'pnpm'
34 |
35 | - name: Install Dependencies
36 | run: pnpm install
37 |
38 | - name: Publish to NPM
39 | run: pnpm -r publish --access public --no-git-checks
40 | env:
41 | NODE_AUTH_TOKEN: ${{secrets.npm_token}}
42 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020-present, Vben
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/core/src/types.ts:
--------------------------------------------------------------------------------
1 | import type { ZlibOptions, BrotliOptions } from 'zlib'
2 |
3 | export type Algorithm = 'gzip' | 'brotliCompress' | 'deflate' | 'deflateRaw'
4 |
5 | export type CompressionOptions = Partial | Partial
6 | export interface VitePluginCompression {
7 | /**
8 | * Log compressed files and their compression ratios.
9 | * @default: true
10 | */
11 | verbose?: boolean
12 | /**
13 | * Minimum file size before compression is used.
14 | * @default 1025
15 | */
16 | threshold?: number
17 | /**
18 | * Filter files that do not need to be compressed
19 | * @default /\.(js|mjs|json|css|html)$/i
20 | */
21 | filter?: RegExp | ((file: string) => boolean)
22 |
23 | /**
24 | * Whether to enable compression
25 | * @default: false
26 | */
27 | disable?: boolean
28 | /**
29 | * Compression algorithm
30 | * @default gzip
31 | */
32 | algorithm?: Algorithm
33 |
34 | /**
35 | * File format after compression
36 | * @default .gz
37 | */
38 | ext?: string
39 |
40 | /**
41 | * Compression Options
42 | */
43 | compressionOptions?: CompressionOptions
44 | /**
45 | * Delete the corresponding source file after compressing the file
46 | * @default: false
47 | */
48 | deleteOriginFile?: boolean
49 |
50 | /**
51 | * success callback after completed
52 | */
53 | success?: () => void
54 | }
55 |
--------------------------------------------------------------------------------
/packages/core/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vite-plugin-compression",
3 | "version": "0.5.1",
4 | "description": "Use gzip or brotli to compress resources.",
5 | "main": "dist/index.cjs",
6 | "module": "dist/index.mjs",
7 | "types": "dist/index.d.ts",
8 | "exports": {
9 | ".": {
10 | "require": "./dist/index.cjs",
11 | "import": "./dist/index.mjs",
12 | "types": "./dist/index.d.ts"
13 | }
14 | },
15 | "license": "MIT",
16 | "author": "Vben",
17 | "files": [
18 | "dist"
19 | ],
20 | "scripts": {
21 | "dev": "pnpm unbuild --stub",
22 | "build": "pnpm unbuild",
23 | "prepublishOnly": "npm run build",
24 | "prepack": "pnpm unbuild"
25 | },
26 | "keywords": [
27 | "vite",
28 | "vite-plugin",
29 | "imagemin",
30 | "vben"
31 | ],
32 | "repository": {
33 | "type": "git",
34 | "url": "https://github.com/anncwb/vite-plugin-compression",
35 | "directory": "packages/core"
36 | },
37 | "bugs": {
38 | "url": "https://github.com/anncwb/vite-plugin-compression/issues"
39 | },
40 | "homepage": "https://github.com/anncwb/vite-plugin-compression/tree/master/#readme",
41 | "dependencies": {
42 | "chalk": "^4.1.2",
43 | "debug": "^4.3.3",
44 | "fs-extra": "^10.0.0"
45 | },
46 | "peerDependencies": {
47 | "vite": ">=2.0.0"
48 | },
49 | "devDependencies": {
50 | "@types/chalk": "^2.2.0",
51 | "@types/debug": "^4.1.7",
52 | "@types/fs-extra": "^9.0.13",
53 | "@types/node": "^17.0.13"
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/packages/core/src/utils.ts:
--------------------------------------------------------------------------------
1 | import fs from 'fs'
2 | import path from 'path'
3 | export const isFunction = (arg: unknown): arg is (...args: any[]) => any =>
4 | typeof arg === 'function'
5 |
6 | export const isRegExp = (arg: unknown): arg is RegExp =>
7 | Object.prototype.toString.call(arg) === '[object RegExp]'
8 |
9 | /*
10 | * Read all files in the specified folder, filter through regular rules, and return file path array
11 | * @param root Specify the folder path
12 | * [@param] reg Regular expression for filtering files, optional parameters
13 | * Note: It can also be deformed to check whether the file path conforms to regular rules. The path can be a folder or a file. The path that does not exist is also fault-tolerant.
14 | */
15 | export function readAllFile(root: string, reg?: RegExp) {
16 | let resultArr: string[] = []
17 | try {
18 | if (fs.existsSync(root)) {
19 | const stat = fs.lstatSync(root)
20 | if (stat.isDirectory()) {
21 | // dir
22 | const files = fs.readdirSync(root)
23 | files.forEach(function (file) {
24 | const t = readAllFile(path.join(root, '/', file), reg)
25 | resultArr = resultArr.concat(t)
26 | })
27 | } else {
28 | if (reg !== undefined) {
29 | if (isFunction(reg.test) && reg.test(root)) {
30 | resultArr.push(root)
31 | }
32 | } else {
33 | resultArr.push(root)
34 | }
35 | }
36 | }
37 | } catch (error) {
38 | throw error
39 | }
40 |
41 | return resultArr
42 | }
43 |
--------------------------------------------------------------------------------
/README.zh_CN.md:
--------------------------------------------------------------------------------
1 | # vite-plugin-compression
2 |
3 | **中文** | [English](./README.md)
4 |
5 | [![npm][npm-img]][npm-url] [![node][node-img]][node-url]
6 |
7 | 使用 `gzip` 或者 `brotli` 来压缩资源.
8 |
9 | 由于[vite-plugin-compress](https://github.com/alloc/vite-plugin-compress)不支持`gzip`压缩,所以独立了一份进行修改,并增加部分功能。
10 |
11 | ## 安装 (yarn or npm)
12 |
13 | **node version:** >=12.0.0
14 |
15 | **vite version:** >=2.0.0
16 |
17 | ```
18 | yarn add vite-plugin-compression -D
19 | ```
20 |
21 | or
22 |
23 | ```
24 | npm i vite-plugin-compression -D
25 | ```
26 |
27 | ## 使用
28 |
29 | - vite.config.ts 中的配置插件
30 |
31 | ```ts
32 | import viteCompression from 'vite-plugin-compression';
33 |
34 | export default () => {
35 | return {
36 | plugins: [viteCompression()],
37 | };
38 | };
39 | ```
40 |
41 | ### 配置说明
42 |
43 | | 参数 | 类型 | 默认值 | 说明 |
44 | | --- | --- | --- | --- |
45 | | verbose | `boolean` | `true` | 是否在控制台输出压缩结果 |
46 | | filter | `RegExp or (file: string) => boolean` | `DefaultFilter` | 指定哪些资源不压缩 |
47 | | disable | `boolean` | `false` | 是否禁用 |
48 | | threshold | `number` | - | 体积大于 threshold 才会被压缩,单位 b |
49 | | algorithm | `string` | `gzip` | 压缩算法,可选 [ 'gzip' , 'brotliCompress' ,'deflate' , 'deflateRaw'] |
50 | | ext | `string` | `.gz` | 生成的压缩包后缀 |
51 | | compressionOptions | `object` | - | 对应的压缩算法的参数 |
52 | | deleteOriginFile | `boolean` | - | 压缩后是否删除源文件 |
53 |
54 | **DefaultFilter**
55 |
56 | `/\.(js|mjs|json|css|html)$/i`
57 |
58 | ## 示例
59 |
60 | **运行示例**
61 |
62 | ```bash
63 |
64 | cd ./example
65 |
66 | yarn install
67 |
68 | yarn test:gzip
69 |
70 | yarn test:br
71 |
72 | ```
73 |
74 | ## 示例项目
75 |
76 | [Vben Admin](https://github.com/anncwb/vue-vben-admin)
77 |
78 | ## License
79 |
80 | MIT
81 |
82 | ## 灵感
83 |
84 | [vite-plugin-compress](https://github.com/alloc/vite-plugin-compress)
85 |
86 | [npm-img]: https://img.shields.io/npm/v/vite-plugin-compression.svg
87 | [npm-url]: https://npmjs.com/package/vite-plugin-compression
88 | [node-img]: https://img.shields.io/node/v/vite-plugin-compression.svg
89 | [node-url]: https://nodejs.org/en/about/releases/
90 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vite-plugin-compression-monorepo",
3 | "private": true,
4 | "version": "0.5.1",
5 | "description": "Use gzip or brotli to compress resources.",
6 | "license": "MIT",
7 | "author": "Vben",
8 | "scripts": {
9 | "stub": "pnpm run prepack --filter ./packages -- --stub",
10 | "postinstall": "pnpm run stub",
11 | "log": "conventional-changelog -p angular -i CHANGELOG.md -s",
12 | "lint:pretty": "pretty-quick --staged",
13 | "lint:eslint": "eslint \"packages/**/*.{ts,tsx}\" --fix",
14 | "prepare": "husky install",
15 | "preinstall": "npx only-allow pnpm",
16 | "test": "vitest"
17 | },
18 | "repository": {
19 | "type": "git",
20 | "url": "https://github.com/anncwb/vite-plugin-compression"
21 | },
22 | "bugs": {
23 | "url": "https://github.com/anncwb/vite-plugin-compression/issues"
24 | },
25 | "homepage": "https://github.com/anncwb/vite-plugin-compression/tree/master/#readme",
26 | "devDependencies": {
27 | "@commitlint/cli": "^16.1.0",
28 | "@commitlint/config-conventional": "^16.0.0",
29 | "@types/html-minifier-terser": "^6.1.0",
30 | "@types/jsdom": "^16.2.14",
31 | "@types/node": "^17.0.13",
32 | "@typescript-eslint/eslint-plugin": "^5.10.1",
33 | "@typescript-eslint/parser": "^5.10.1",
34 | "commitizen": "^4.2.4",
35 | "conventional-changelog-cli": "^2.2.2",
36 | "cross-env": "^7.0.3",
37 | "eslint": "^8.7.0",
38 | "eslint-config-prettier": "^8.3.0",
39 | "eslint-plugin-html": "^6.2.0",
40 | "husky": "^7.0.4",
41 | "lint-staged": "^12.3.2",
42 | "prettier": "^2.5.1",
43 | "rimraf": "^3.0.2",
44 | "tsup": "^5.11.11",
45 | "typescript": "^4.5.5",
46 | "unbuild": "^0.6.9",
47 | "vite": "^2.7.13",
48 | "vitest": "^0.2.5"
49 | },
50 | "lint-staged": {
51 | "*": [
52 | "prettier --write --ignore-unknown"
53 | ],
54 | "packages/*/{src,types}/**/*.ts": [
55 | "eslint --ext .ts"
56 | ],
57 | "packages/**/*.d.ts": [
58 | "eslint --ext .ts"
59 | ]
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vite-plugin-compression
2 |
3 | **English** | [中文](./README.zh_CN.md)
4 |
5 | [![npm][npm-img]][npm-url] [![node][node-img]][node-url]
6 |
7 | Use `gzip` or `brotli` to compress resources.
8 |
9 | Since [vite-plugin-compress](https://github.com/alloc/vite-plugin-compress) does not support `gzip` compression, a separate copy has been modified and some functions have been added.
10 |
11 | ## Install (yarn or npm)
12 |
13 | **node version:** >=12.0.0
14 |
15 | **vite version:** >=2.0.0
16 |
17 | ```
18 | yarn add vite-plugin-compression -D
19 | ```
20 |
21 | or
22 |
23 | ```
24 | npm i vite-plugin-compression -D
25 | ```
26 |
27 | ## Usage
28 |
29 | - Configuration plugin in vite.config.ts
30 |
31 | ```ts
32 | import viteCompression from 'vite-plugin-compression';
33 |
34 | export default () => {
35 | return {
36 | plugins: [viteCompression()],
37 | };
38 | };
39 | ```
40 |
41 | ### Options
42 |
43 | | params | type | default | default |
44 | | --- | --- | --- | --- |
45 | | verbose | `boolean` | `true` | Whether to output the compressed result in the console |
46 | | filter | `RegExp or (file: string) => boolean` | `DefaultFilter` | Specify which resources are not compressed |
47 | | disable | `boolean` | `false` | Whether to disable |
48 | | threshold | `number` | `1025` | It will be compressed if the volume is larger than threshold, the unit is b |
49 | | algorithm | `string` | `gzip` | Compression algorithm, optional ['gzip','brotliCompress' ,'deflate','deflateRaw'] |
50 | | ext | `string` | `.gz` | Suffix of the generated compressed package |
51 | | compressionOptions | `object` | - | The parameters of the corresponding compression algorithm |
52 | | deleteOriginFile | `boolean` | - | Whether to delete source files after compression |
53 |
54 | **DefaultFilter**
55 |
56 | `/\.(js|mjs|json|css|html)$/i`
57 |
58 | ## Example
59 |
60 | **Run Example**
61 |
62 | ```bash
63 |
64 | cd ./example
65 |
66 | yarn install
67 |
68 | yarn test:gzip
69 |
70 | yarn test:br
71 |
72 | ```
73 |
74 | ## Sample project
75 |
76 | [Vben Admin](https://github.com/anncwb/vue-vben-admin)
77 |
78 | ## License
79 |
80 | MIT
81 |
82 | ## Inspiration
83 |
84 | [vite-plugin-compress](https://github.com/alloc/vite-plugin-compress)
85 |
86 | [npm-img]: https://img.shields.io/npm/v/vite-plugin-compression.svg
87 | [npm-url]: https://npmjs.com/package/vite-plugin-compression
88 | [node-img]: https://img.shields.io/node/v/vite-plugin-compression.svg
89 | [node-url]: https://nodejs.org/en/about/releases/
90 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "typescript.tsdk": "./node_modules/typescript/lib",
3 | "typescript.enablePromptUseWorkspaceTsdk": true,
4 | //===========================================
5 | //============= Editor ======================
6 | //===========================================
7 | "explorer.openEditors.visible": 0,
8 | "editor.tabSize": 2,
9 | "editor.defaultFormatter": "esbenp.prettier-vscode",
10 | "diffEditor.ignoreTrimWhitespace": false,
11 | //===========================================
12 | //============= Other =======================
13 | //===========================================
14 | "breadcrumbs.enabled": true,
15 | "open-in-browser.default": "chrome",
16 | //===========================================
17 | //============= files =======================
18 | //===========================================
19 | "files.eol": "\n",
20 | "search.exclude": {
21 | "**/node_modules": true,
22 | "**/*.log": true,
23 | "**/*.log*": true,
24 | "**/bower_components": true,
25 | "**/dist": true,
26 | "**/elehukouben": true,
27 | "**/.git": true,
28 | "**/.gitignore": true,
29 | "**/.svn": true,
30 | "**/.DS_Store": true,
31 | "**/.idea": true,
32 | "**/.vscode": false,
33 | "**/yarn.lock": true,
34 | "**/tmp": true,
35 | "out": true,
36 | "dist": true,
37 | "node_modules": true,
38 | "CHANGELOG.md": true,
39 | "examples": true,
40 | "res": true,
41 | "screenshots": true,
42 | "yarn-error.log": true,
43 | "**/pnpm-lock.yaml": true,
44 | "**/.yarn": true
45 | },
46 | "files.exclude": {
47 | "**/.cache": true,
48 | "**/.editorconfig": true,
49 | "**/.eslintcache": true,
50 | "**/bower_components": true,
51 | "**/.idea": true,
52 | "**/tmp": true,
53 | "**/.git": true,
54 | "**/.svn": true,
55 | "**/.hg": true,
56 | "**/CVS": true,
57 | "**/.DS_Store": true
58 | },
59 | "files.watcherExclude": {
60 | "**/.git/objects/**": true,
61 | "**/.git/subtree-cache/**": true,
62 | "**/.vscode/**": true,
63 | "**/node_modules/**": true,
64 | "**/tmp/**": true,
65 | "**/bower_components/**": true,
66 | "**/dist/**": true,
67 | "**/yarn.lock": true
68 | },
69 | "stylelint.enable": true,
70 | "stylelint.packageManager": "yarn",
71 | "liveServer.settings.donotShowInfoMsg": true,
72 | "workbench.settings.enableNaturalLanguageSearch": false,
73 | "prettier.requireConfig": true,
74 | "workbench.sideBar.location": "left",
75 | "cSpell.words": [
76 | "vben",
77 | "windi",
78 | "browserslist",
79 | "tailwindcss",
80 | "esnext",
81 | "antv",
82 | "tinymce",
83 | "qrcode",
84 | "sider",
85 | "pinia",
86 | "sider",
87 | "nprogress",
88 | "INTLIFY",
89 | "stylelint",
90 | "esno",
91 | "vitejs",
92 | "sortablejs",
93 | "mockjs",
94 | "codemirror",
95 | "iconify",
96 | "commitlint",
97 | "vditor",
98 | "echarts",
99 | "cropperjs",
100 | "logicflow",
101 | "vueuse",
102 | "zxcvbn",
103 | "lintstagedrc",
104 | "brotli",
105 | "tailwindcss",
106 | "sider"
107 | ]
108 | }
109 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [0.5.1](https://github.com/anncwb/vite-plugin-compression/compare/v0.5.0...v0.5.1) (2022-01-29)
2 |
3 | ### Bug Fixes
4 |
5 | - build error,close [#13](https://github.com/anncwb/vite-plugin-compression/issues/13) ([dbc3247](https://github.com/anncwb/vite-plugin-compression/commit/dbc32478e7f03ee56b73185dcb4c76fad712bb55))
6 | - return a promise to have it awaited ([24b77ae](https://github.com/anncwb/vite-plugin-compression/commit/24b77ae1917e196b9602ed268330cf5a86b09c55))
7 |
8 | # [0.5.0](https://github.com/anncwb/vite-plugin-compression/compare/v0.3.6...v0.5.0) (2022-01-28)
9 |
10 | # [0.4.0](https://github.com/anncwb/vite-plugin-compression/compare/v0.3.6...v0.4.0) (2021-12-28)
11 |
12 | ## [0.3.5](https://github.com/anncwb/vite-plugin-compression/compare/v0.2.4...v0.3.5) (2021-11-25)
13 |
14 | ### Bug Fixes
15 |
16 | - type error,fix [#5](https://github.com/anncwb/vite-plugin-compression/issues/5) ([cd6c313](https://github.com/anncwb/vite-plugin-compression/commit/cd6c313f63b4a8147d518e17c887049cdae6f1b7))
17 |
18 | ## [0.3.4](https://github.com/anncwb/vite-plugin-compression/compare/v0.2.4...v0.3.4) (2021-08-23)
19 |
20 | ### Bug Fixes
21 |
22 | - type error,fix [#5](https://github.com/anncwb/vite-plugin-compression/issues/5) ([cd6c313](https://github.com/anncwb/vite-plugin-compression/commit/cd6c313f63b4a8147d518e17c887049cdae6f1b7))
23 |
24 | ## [0.3.3](https://github.com/anncwb/vite-plugin-compression/compare/v0.3.2...v0.3.3) (2021-08-01)
25 |
26 | ## [0.3.2](https://github.com/anncwb/vite-plugin-compression/compare/v0.2.4...v0.3.2) (2021-08-01)
27 |
28 | ### Bug Fixes
29 |
30 | - type error,fix [#5](https://github.com/anncwb/vite-plugin-compression/issues/5) ([cd6c313](https://github.com/anncwb/vite-plugin-compression/commit/cd6c313f63b4a8147d518e17c887049cdae6f1b7))
31 |
32 | ## [0.3.1](https://github.com/anncwb/vite-plugin-compression/compare/v0.2.4...v0.3.1) (2021-07-18)
33 |
34 | ### Bug Fixes
35 |
36 | - type error,fix [#5](https://github.com/anncwb/vite-plugin-compression/issues/5) ([cd6c313](https://github.com/anncwb/vite-plugin-compression/commit/cd6c313f63b4a8147d518e17c887049cdae6f1b7))
37 |
38 | ## [0.3.1](https://github.com/anncwb/vite-plugin-compression/compare/v0.2.4...v0.3.1) (2021-07-18)
39 |
40 | ### Bug Fixes
41 |
42 | - type error,fix [#5](https://github.com/anncwb/vite-plugin-compression/issues/5) ([cd6c313](https://github.com/anncwb/vite-plugin-compression/commit/cd6c313f63b4a8147d518e17c887049cdae6f1b7))
43 |
44 | ## [0.2.6](https://github.com/anncwb/vite-plugin-compression/compare/v0.2.4...v0.2.6) (2021-07-18)
45 |
46 | ### Bug Fixes
47 |
48 | - type error,fix [#5](https://github.com/anncwb/vite-plugin-compression/issues/5) ([cd6c313](https://github.com/anncwb/vite-plugin-compression/commit/cd6c313f63b4a8147d518e17c887049cdae6f1b7))
49 |
50 | ## [0.2.5](https://github.com/anncwb/vite-plugin-compression/compare/v0.2.4...v0.2.5) (2021-05-09)
51 |
52 | ## [0.2.4](https://github.com/anncwb/vite-plugin-compression/compare/v0.2.2...v0.2.4) (2021-04-03)
53 |
54 | ## [0.2.3](https://github.com/anncwb/vite-plugin-compression/compare/v0.2.2...v0.2.3) (2021-03-02)
55 |
56 | ## 0.2.2 (2021-02-23)
57 |
58 | ### Features
59 |
60 | - add github action ([6ea8811](https://github.com/anncwb/vite-plugin-compression/commit/6ea8811e7d554fc0f821e1f4f034ce5b1388e105))
61 |
62 | ## 0.2.1 (2021-02-23)
63 |
--------------------------------------------------------------------------------
/packages/core/src/index.ts:
--------------------------------------------------------------------------------
1 | import type { Plugin, ResolvedConfig } from 'vite'
2 | import type { CompressionOptions, VitePluginCompression } from './types'
3 | import path from 'path'
4 | import { normalizePath } from 'vite'
5 | import { readAllFile, isRegExp, isFunction } from './utils'
6 | import fs from 'fs-extra'
7 | import chalk from 'chalk'
8 | import zlib from 'zlib'
9 | import Debug from 'debug'
10 |
11 | const debug = Debug.debug('vite-plugin-compression')
12 |
13 | const extRE = /\.(js|mjs|json|css|html)$/i
14 |
15 | const mtimeCache = new Map()
16 |
17 | export default function (options: VitePluginCompression = {}): Plugin {
18 | let outputPath: string
19 | let config: ResolvedConfig
20 |
21 | const emptyPlugin: Plugin = {
22 | name: 'vite:compression',
23 | }
24 |
25 | const {
26 | disable = false,
27 | filter = extRE,
28 | verbose = true,
29 | threshold = 1025,
30 | compressionOptions = {},
31 | deleteOriginFile = false,
32 | // eslint-disable-next-line
33 | success = () => {},
34 | } = options
35 |
36 | let { ext = '' } = options
37 | const { algorithm = 'gzip' } = options
38 |
39 | if (algorithm === 'gzip' && !ext) {
40 | ext = '.gz'
41 | }
42 |
43 | if (algorithm === 'brotliCompress' && !ext) {
44 | ext = '.br'
45 | }
46 |
47 | if (disable) {
48 | return emptyPlugin
49 | }
50 |
51 | debug('plugin options:', options)
52 |
53 | return {
54 | ...emptyPlugin,
55 | apply: 'build',
56 | enforce: 'post',
57 | configResolved(resolvedConfig) {
58 | config = resolvedConfig
59 | outputPath = path.isAbsolute(config.build.outDir)
60 | ? config.build.outDir
61 | : path.join(config.root, config.build.outDir)
62 | debug('resolvedConfig:', resolvedConfig)
63 | },
64 | async closeBundle() {
65 | let files = readAllFile(outputPath) || []
66 | debug('files:', files)
67 |
68 | if (!files.length) return
69 |
70 | files = filterFiles(files, filter)
71 |
72 | const compressOptions = getCompressionOptions(
73 | algorithm,
74 | compressionOptions,
75 | )
76 |
77 | const compressMap = new Map<
78 | string,
79 | { size: number; oldSize: number; cname: string }
80 | >()
81 |
82 | const handles = files.map(async (filePath: string) => {
83 | const { mtimeMs, size: oldSize } = await fs.stat(filePath)
84 | if (mtimeMs <= (mtimeCache.get(filePath) || 0) || oldSize < threshold)
85 | return
86 |
87 | let content = await fs.readFile(filePath)
88 |
89 | if (deleteOriginFile) {
90 | fs.remove(filePath)
91 | }
92 |
93 | try {
94 | content = await compress(content, algorithm, compressOptions)
95 | } catch (error) {
96 | config.logger.error('compress error:' + filePath)
97 | }
98 | const size = content.byteLength
99 |
100 | const cname = getOutputFileName(filePath, ext)
101 | compressMap.set(filePath, {
102 | size: size / 1024,
103 | oldSize: oldSize / 1024,
104 | cname: cname,
105 | })
106 | await fs.writeFile(cname, content)
107 |
108 | mtimeCache.set(filePath, Date.now())
109 | })
110 |
111 | return Promise.all(handles).then(() => {
112 | if (verbose) {
113 | handleOutputLogger(config, compressMap, algorithm)
114 | success()
115 | }
116 | })
117 | },
118 | }
119 | }
120 |
121 | function filterFiles(
122 | files: string[],
123 | filter: RegExp | ((file: string) => boolean),
124 | ) {
125 | if (filter) {
126 | const isRe = isRegExp(filter)
127 | const isFn = isFunction(filter)
128 | files = files.filter((file) => {
129 | if (isRe) {
130 | return (filter as RegExp).test(file)
131 | }
132 | if (isFn) {
133 | // eslint-disable-next-line
134 | return (filter as Function)(file)
135 | }
136 | return true
137 | })
138 | }
139 | return files
140 | }
141 |
142 | /**
143 | * get common options
144 | */
145 | function getCompressionOptions(
146 | algorithm = '',
147 | compressionOptions: CompressionOptions = {},
148 | ) {
149 | const defaultOptions: {
150 | [key: string]: Record
151 | } = {
152 | gzip: {
153 | level: zlib.constants.Z_BEST_COMPRESSION,
154 | },
155 | deflate: {
156 | level: zlib.constants.Z_BEST_COMPRESSION,
157 | },
158 | deflateRaw: {
159 | level: zlib.constants.Z_BEST_COMPRESSION,
160 | },
161 | brotliCompress: {
162 | params: {
163 | [zlib.constants.BROTLI_PARAM_QUALITY]:
164 | zlib.constants.BROTLI_MAX_QUALITY,
165 | [zlib.constants.BROTLI_PARAM_MODE]: zlib.constants.BROTLI_MODE_TEXT,
166 | },
167 | },
168 | }
169 | return {
170 | ...defaultOptions[algorithm],
171 | ...compressionOptions,
172 | } as CompressionOptions
173 | }
174 |
175 | /**
176 | * Compression core method
177 | * @param content
178 | * @param algorithm
179 | * @param options
180 | */
181 | function compress(
182 | content: Buffer,
183 | algorithm: 'gzip' | 'brotliCompress' | 'deflate' | 'deflateRaw',
184 | options: CompressionOptions = {},
185 | ) {
186 | return new Promise((resolve, reject) => {
187 | // @ts-ignore
188 | zlib[algorithm](content, options, (err, result) =>
189 | err ? reject(err) : resolve(result),
190 | )
191 | })
192 | }
193 |
194 | /**
195 | * Get the suffix
196 | * @param filepath
197 | * @param ext
198 | */
199 | function getOutputFileName(filepath: string, ext: string) {
200 | const compressExt = ext.startsWith('.') ? ext : `.${ext}`
201 | return `${filepath}${compressExt}`
202 | }
203 |
204 | // Packed output logic
205 | function handleOutputLogger(
206 | config: ResolvedConfig,
207 | compressMap: Map,
208 | algorithm: string,
209 | ) {
210 | config.logger.info(
211 | `\n${chalk.cyan('✨ [vite-plugin-compression]:algorithm=' + algorithm)}` +
212 | ` - compressed file successfully: `,
213 | )
214 |
215 | const keyLengths = Array.from(compressMap.keys(), (name) => name.length)
216 |
217 | const maxKeyLength = Math.max(...keyLengths)
218 | compressMap.forEach((value, name) => {
219 | const { size, oldSize, cname } = value
220 |
221 | const rName = normalizePath(cname).replace(
222 | normalizePath(`${config.build.outDir}/`),
223 | '',
224 | )
225 |
226 | const sizeStr = `${oldSize.toFixed(2)}kb / ${algorithm}: ${size.toFixed(
227 | 2,
228 | )}kb`
229 |
230 | config.logger.info(
231 | chalk.dim(path.basename(config.build.outDir) + '/') +
232 | chalk.blueBright(rName) +
233 | ' '.repeat(2 + maxKeyLength - name.length) +
234 | ' ' +
235 | chalk.dim(sizeStr),
236 | )
237 | })
238 | config.logger.info('\n')
239 | }
240 |
--------------------------------------------------------------------------------
/packages/playground/basic/src/assets/test.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------