zcompress-vite-plugin 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -29
- package/index.d.ts +2 -0
- package/index.js +54 -36
- package/install.js +14 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Vite 生产构建资源压缩插件 — gzip / zstd / brotli,多线程并行
|
|
|
8
8
|
npm install zcompress-vite-plugin --save-dev
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
安装后会自动下载对应平台的 `zcompress` 二进制。下载失败可手动安装:
|
|
12
12
|
|
|
13
13
|
| 平台 | 命令 |
|
|
14
14
|
|------|------|
|
|
@@ -35,17 +35,7 @@ export default {
|
|
|
35
35
|
};
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
`vite build` 后,`dist-compressed/`
|
|
39
|
-
|
|
40
|
-
```
|
|
41
|
-
dist-compressed/
|
|
42
|
-
├── index.html.gz
|
|
43
|
-
├── style.css.gz
|
|
44
|
-
├── app.js.gz
|
|
45
|
-
└── logo.svg.gz
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
配合 Nginx 的 `gzip_static on` 直接使用。
|
|
38
|
+
`vite build` 后,`dist-compressed/` 目录下就是压缩后的文件。
|
|
49
39
|
|
|
50
40
|
## 选项
|
|
51
41
|
|
|
@@ -56,13 +46,35 @@ dist-compressed/
|
|
|
56
46
|
| `threads` | `number` | `0` | 线程数 (0=CPU 核心数) |
|
|
57
47
|
| `verbose` | `boolean` | `false` | 显示详细输出 |
|
|
58
48
|
| `cache` | `boolean` | `false` | 跳过未修改文件 |
|
|
59
|
-
| `include` | `string[]` | `[]` |
|
|
60
|
-
| `exclude` | `string[]` | `[]` |
|
|
49
|
+
| `include` | `string[]` | `[]` | 额外压缩扩展名 |
|
|
50
|
+
| `exclude` | `string[]` | `[]` | 排除扩展名 |
|
|
61
51
|
| `binaryPath` | `string` | 自动 | 手动指定二进制路径 |
|
|
52
|
+
| `failOnError` | `boolean` | `true` | 压缩失败时让 `vite build` 失败 |
|
|
62
53
|
|
|
63
|
-
##
|
|
54
|
+
## 故障排查
|
|
55
|
+
|
|
56
|
+
### `postinstall` 下载二进制 404
|
|
57
|
+
|
|
58
|
+
如果看到类似:
|
|
59
|
+
|
|
60
|
+
```txt
|
|
61
|
+
https://github.com/luochuan2008/zcompress/releases/download/vX.Y.Z/zcompress-macos-arm64 → 404
|
|
62
|
+
```
|
|
64
63
|
|
|
65
|
-
|
|
64
|
+
说明 npm 版本已发布,但对应 GitHub Release 还没上传该平台二进制。
|
|
65
|
+
|
|
66
|
+
解决方式:
|
|
67
|
+
|
|
68
|
+
1. 手动安装 CLI(上面安装表)
|
|
69
|
+
2. 在插件里指定 `binaryPath`
|
|
70
|
+
|
|
71
|
+
```js
|
|
72
|
+
zcompress({
|
|
73
|
+
binaryPath: '/absolute/path/to/zcompress',
|
|
74
|
+
})
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
默认 `failOnError: true`,失败会直接中断构建(不会静默跳过压缩)。
|
|
66
78
|
|
|
67
79
|
## License
|
|
68
80
|
|
|
@@ -80,11 +92,16 @@ High-performance multi-threaded asset compression for Vite production builds. 5-
|
|
|
80
92
|
npm install zcompress-vite-plugin --save-dev
|
|
81
93
|
```
|
|
82
94
|
|
|
83
|
-
The
|
|
95
|
+
The package auto-downloads a platform binary. If that fails, install/build manually:
|
|
84
96
|
|
|
85
97
|
```bash
|
|
86
|
-
|
|
87
|
-
|
|
98
|
+
# macOS
|
|
99
|
+
brew install zstd brotli
|
|
100
|
+
zig build -Doptimize=ReleaseFast
|
|
101
|
+
|
|
102
|
+
# Linux
|
|
103
|
+
sudo apt-get install -y libzstd-dev libbrotli-dev
|
|
104
|
+
zig build -Doptimize=ReleaseFast
|
|
88
105
|
```
|
|
89
106
|
|
|
90
107
|
## Usage
|
|
@@ -97,7 +114,7 @@ export default {
|
|
|
97
114
|
plugins: [
|
|
98
115
|
zcompress({
|
|
99
116
|
algo: 'brotli', // 'gzip' | 'zstd' | 'brotli'
|
|
100
|
-
level:
|
|
117
|
+
level: 9, // 1-9
|
|
101
118
|
threads: 0, // 0 = auto
|
|
102
119
|
cache: true, // skip unchanged files
|
|
103
120
|
})
|
|
@@ -105,15 +122,7 @@ export default {
|
|
|
105
122
|
};
|
|
106
123
|
```
|
|
107
124
|
|
|
108
|
-
After `vite build`,
|
|
109
|
-
|
|
110
|
-
```
|
|
111
|
-
dist-compressed/
|
|
112
|
-
├── index.html.br
|
|
113
|
-
├── style.css.br
|
|
114
|
-
├── app.js.br
|
|
115
|
-
└── logo.svg.br
|
|
116
|
-
```
|
|
125
|
+
After `vite build`, compressed assets are written to `dist-compressed/`.
|
|
117
126
|
|
|
118
127
|
## Options
|
|
119
128
|
|
|
@@ -127,6 +136,32 @@ dist-compressed/
|
|
|
127
136
|
| `include` | `string[]` | `[]` | Extra extensions to compress |
|
|
128
137
|
| `exclude` | `string[]` | `[]` | Extensions to skip |
|
|
129
138
|
| `binaryPath` | `string` | auto | Override binary path |
|
|
139
|
+
| `failOnError` | `boolean` | `true` | Fail `vite build` when compression fails |
|
|
140
|
+
|
|
141
|
+
## Troubleshooting
|
|
142
|
+
|
|
143
|
+
### `postinstall` binary download returns 404
|
|
144
|
+
|
|
145
|
+
If you see:
|
|
146
|
+
|
|
147
|
+
```txt
|
|
148
|
+
https://github.com/luochuan2008/zcompress/releases/download/vX.Y.Z/zcompress-macos-arm64 → 404
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
that npm version exists, but the matching GitHub Release binary asset is missing.
|
|
152
|
+
|
|
153
|
+
Workarounds:
|
|
154
|
+
|
|
155
|
+
1. Install/build the CLI manually
|
|
156
|
+
2. Set `binaryPath` explicitly
|
|
157
|
+
|
|
158
|
+
```js
|
|
159
|
+
zcompress({
|
|
160
|
+
binaryPath: '/absolute/path/to/zcompress',
|
|
161
|
+
})
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Default `failOnError: true` ensures builds fail loudly (instead of silently skipping compression).
|
|
130
165
|
|
|
131
166
|
## License
|
|
132
167
|
|
package/index.d.ts
CHANGED
|
@@ -16,6 +16,8 @@ declare module 'zcompress-vite-plugin' {
|
|
|
16
16
|
exclude?: string[];
|
|
17
17
|
/** Override path to zcompress binary */
|
|
18
18
|
binaryPath?: string;
|
|
19
|
+
/** Fail Vite build if compression fails (default: true) */
|
|
20
|
+
failOnError?: boolean;
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
import type { Plugin } from 'vite';
|
package/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* zcompress Vite Plugin
|
|
3
3
|
*
|
|
4
4
|
* A Vite plugin that compresses build output using the zcompress CLI.
|
|
5
|
-
* Provides multi-threaded gzip/zstd compression for production builds.
|
|
5
|
+
* Provides multi-threaded gzip/zstd/brotli compression for production builds.
|
|
6
6
|
*
|
|
7
7
|
* @example
|
|
8
8
|
* // vite.config.js
|
|
@@ -15,9 +15,12 @@
|
|
|
15
15
|
* };
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import {
|
|
19
|
-
import { existsSync, statSync } from 'node:fs';
|
|
20
|
-
import { join } from 'node:path';
|
|
18
|
+
import { execFileSync } from 'node:child_process';
|
|
19
|
+
import { existsSync, statSync, readdirSync } from 'node:fs';
|
|
20
|
+
import { join, dirname, isAbsolute } from 'node:path';
|
|
21
|
+
import { fileURLToPath } from 'node:url';
|
|
22
|
+
|
|
23
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
21
24
|
|
|
22
25
|
/**
|
|
23
26
|
* @typedef {Object} ZCompressOptions
|
|
@@ -29,6 +32,7 @@ import { join } from 'node:path';
|
|
|
29
32
|
* @property {string[]} [include] - Extra file extensions to include
|
|
30
33
|
* @property {string[]} [exclude] - File extensions to exclude
|
|
31
34
|
* @property {string} [binaryPath] - Path to zcompress binary
|
|
35
|
+
* @property {boolean} [failOnError=true] - Fail Vite build if compression fails
|
|
32
36
|
*/
|
|
33
37
|
|
|
34
38
|
/** @type {ZCompressOptions} */
|
|
@@ -40,19 +44,19 @@ const DEFAULT_OPTIONS = {
|
|
|
40
44
|
cache: false,
|
|
41
45
|
include: [],
|
|
42
46
|
exclude: [],
|
|
47
|
+
failOnError: true,
|
|
43
48
|
};
|
|
44
49
|
|
|
45
50
|
/**
|
|
46
51
|
* Find the zcompress binary.
|
|
47
|
-
* Order: 1) downloaded by postinstall
|
|
52
|
+
* Order: 1) downloaded by postinstall 2) local zig build 3) PATH
|
|
48
53
|
*/
|
|
49
54
|
function findBinary() {
|
|
50
|
-
// 1
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
if (existsSync(localBin)) return localBin;
|
|
55
|
+
// 1) postinstall-downloaded binary inside package
|
|
56
|
+
const packagedBin = join(__dirname, 'bin', process.platform === 'win32' ? 'zcompress.exe' : 'zcompress');
|
|
57
|
+
if (existsSync(packagedBin)) return packagedBin;
|
|
54
58
|
|
|
55
|
-
// 2
|
|
59
|
+
// 2) local zig build
|
|
56
60
|
const zigPaths = [
|
|
57
61
|
join(process.cwd(), 'zig-out', 'bin', 'zcompress'),
|
|
58
62
|
join(process.cwd(), 'zig-out', 'bin', 'zcompress.exe'),
|
|
@@ -61,7 +65,7 @@ function findBinary() {
|
|
|
61
65
|
if (existsSync(p)) return p;
|
|
62
66
|
}
|
|
63
67
|
|
|
64
|
-
// 3
|
|
68
|
+
// 3) PATH
|
|
65
69
|
return 'zcompress';
|
|
66
70
|
}
|
|
67
71
|
|
|
@@ -74,20 +78,29 @@ function findBinary() {
|
|
|
74
78
|
export default function zcompressPlugin(userOptions = {}) {
|
|
75
79
|
const options = { ...DEFAULT_OPTIONS, ...userOptions };
|
|
76
80
|
|
|
81
|
+
// ESM-safe plugin state (do not use `this`)
|
|
82
|
+
let active = false;
|
|
83
|
+
let resolvedOutDir = 'dist';
|
|
84
|
+
|
|
77
85
|
return {
|
|
78
86
|
name: 'zcompress',
|
|
79
87
|
apply: 'build',
|
|
80
88
|
enforce: 'post',
|
|
81
89
|
|
|
82
90
|
configResolved(config) {
|
|
83
|
-
|
|
84
|
-
|
|
91
|
+
active = config.command === 'build';
|
|
92
|
+
|
|
93
|
+
// Save outDir from Vite config (Bug #2 fix)
|
|
94
|
+
const configuredOutDir = config.build?.outDir || 'dist';
|
|
95
|
+
resolvedOutDir = isAbsolute(configuredOutDir)
|
|
96
|
+
? configuredOutDir
|
|
97
|
+
: join(config.root || process.cwd(), configuredOutDir);
|
|
85
98
|
},
|
|
86
99
|
|
|
87
100
|
closeBundle() {
|
|
88
|
-
if (!
|
|
101
|
+
if (!active) return;
|
|
89
102
|
|
|
90
|
-
const outDir =
|
|
103
|
+
const outDir = resolvedOutDir;
|
|
91
104
|
const compressedDir = `${outDir}-compressed`;
|
|
92
105
|
|
|
93
106
|
if (!existsSync(outDir)) {
|
|
@@ -103,18 +116,12 @@ export default function zcompressPlugin(userOptions = {}) {
|
|
|
103
116
|
'-l', String(options.level),
|
|
104
117
|
];
|
|
105
118
|
|
|
106
|
-
if (options.threads > 0)
|
|
107
|
-
args.push('-t', String(options.threads));
|
|
108
|
-
}
|
|
119
|
+
if (options.threads > 0) args.push('-t', String(options.threads));
|
|
109
120
|
if (options.verbose) args.push('--verbose');
|
|
110
121
|
if (options.cache) args.push('--cache');
|
|
111
122
|
|
|
112
|
-
for (const ext of options.include) {
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
for (const ext of options.exclude) {
|
|
116
|
-
args.push(`--exclude=${ext}`);
|
|
117
|
-
}
|
|
123
|
+
for (const ext of options.include) args.push(`--include=${ext}`);
|
|
124
|
+
for (const ext of options.exclude) args.push(`--exclude=${ext}`);
|
|
118
125
|
|
|
119
126
|
const cmd = `${binary} ${args.join(' ')}`;
|
|
120
127
|
|
|
@@ -125,10 +132,9 @@ export default function zcompressPlugin(userOptions = {}) {
|
|
|
125
132
|
const startTime = Date.now();
|
|
126
133
|
|
|
127
134
|
try {
|
|
128
|
-
|
|
135
|
+
execFileSync(binary, args, { stdio: 'inherit' });
|
|
129
136
|
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
130
137
|
|
|
131
|
-
// Calculate size stats
|
|
132
138
|
const srcSize = getDirSize(outDir);
|
|
133
139
|
const destSize = getDirSize(compressedDir);
|
|
134
140
|
const savedPct = srcSize > 0
|
|
@@ -137,8 +143,18 @@ export default function zcompressPlugin(userOptions = {}) {
|
|
|
137
143
|
|
|
138
144
|
console.log(`[zcompress] ✅ Compressed ${formatSize(srcSize)} → ${formatSize(destSize)} (saved ${savedPct}%) in ${elapsed}s`);
|
|
139
145
|
} catch (err) {
|
|
140
|
-
|
|
141
|
-
|
|
146
|
+
const message = [
|
|
147
|
+
`[zcompress] ❌ Compression failed: ${err.message}`,
|
|
148
|
+
`[zcompress] Binary used: ${binary}`,
|
|
149
|
+
'[zcompress] If this is a 404 download issue, GitHub Release may be missing prebuilt binaries for this version.',
|
|
150
|
+
'[zcompress] Workaround: install CLI manually (`zig build -Doptimize=ReleaseFast`) and set `binaryPath` in plugin options.',
|
|
151
|
+
].join('\n');
|
|
152
|
+
|
|
153
|
+
if (options.failOnError !== false) {
|
|
154
|
+
throw new Error(message);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
console.error(message);
|
|
142
158
|
}
|
|
143
159
|
},
|
|
144
160
|
};
|
|
@@ -152,17 +168,19 @@ export default function zcompressPlugin(userOptions = {}) {
|
|
|
152
168
|
*/
|
|
153
169
|
function getDirSize(dirPath) {
|
|
154
170
|
if (!existsSync(dirPath)) return 0;
|
|
155
|
-
|
|
171
|
+
|
|
156
172
|
let total = 0;
|
|
157
173
|
try {
|
|
158
|
-
const { readdirSync } = require('node:fs');
|
|
159
|
-
const { join } = require('node:path');
|
|
160
174
|
walkDir(dirPath, (filePath) => {
|
|
161
175
|
try {
|
|
162
176
|
total += statSync(filePath).size;
|
|
163
|
-
} catch
|
|
177
|
+
} catch {
|
|
178
|
+
// ignore unreadable files
|
|
179
|
+
}
|
|
164
180
|
});
|
|
165
|
-
} catch
|
|
181
|
+
} catch {
|
|
182
|
+
// ignore traversal failures
|
|
183
|
+
}
|
|
166
184
|
return total;
|
|
167
185
|
}
|
|
168
186
|
|
|
@@ -173,8 +191,6 @@ function getDirSize(dirPath) {
|
|
|
173
191
|
* @param {(path: string) => void} fn
|
|
174
192
|
*/
|
|
175
193
|
function walkDir(dir, fn) {
|
|
176
|
-
const { readdirSync } = require('node:fs');
|
|
177
|
-
const { join } = require('node:path');
|
|
178
194
|
try {
|
|
179
195
|
const entries = readdirSync(dir, { withFileTypes: true });
|
|
180
196
|
for (const entry of entries) {
|
|
@@ -185,7 +201,9 @@ function walkDir(dir, fn) {
|
|
|
185
201
|
fn(full);
|
|
186
202
|
}
|
|
187
203
|
}
|
|
188
|
-
} catch
|
|
204
|
+
} catch {
|
|
205
|
+
// ignore traversal failures
|
|
206
|
+
}
|
|
189
207
|
}
|
|
190
208
|
|
|
191
209
|
/**
|
package/install.js
CHANGED
|
@@ -5,8 +5,7 @@
|
|
|
5
5
|
* Falls back gracefully if the binary can't be downloaded (e.g. air-gapped, unsupported platform).
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
import { createWriteStream, existsSync, chmodSync, mkdirSync, unlinkSync } from 'node:fs';
|
|
8
|
+
import { createWriteStream, existsSync, chmodSync, mkdirSync, unlinkSync, readFileSync } from 'node:fs';
|
|
10
9
|
import { get } from 'node:https';
|
|
11
10
|
import { join, dirname } from 'node:path';
|
|
12
11
|
import { fileURLToPath } from 'node:url';
|
|
@@ -14,8 +13,11 @@ import { pipeline } from 'node:stream/promises';
|
|
|
14
13
|
|
|
15
14
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
15
|
const BIN_DIR = join(__dirname, 'bin');
|
|
17
|
-
const
|
|
18
|
-
|
|
16
|
+
const REPO = 'luochuan2008/zcompress';
|
|
17
|
+
|
|
18
|
+
// Read package version dynamically to avoid manual sync bugs.
|
|
19
|
+
const pkgJson = JSON.parse(readFileSync(join(__dirname, 'package.json'), 'utf8'));
|
|
20
|
+
const VERSION = pkgJson.version;
|
|
19
21
|
|
|
20
22
|
const PLATFORM_MAP = {
|
|
21
23
|
'darwin-arm64': 'zcompress-macos-arm64',
|
|
@@ -84,7 +86,14 @@ async function downloadBinary() {
|
|
|
84
86
|
console.log(`[zcompress] ✓ Binary installed: ${destPath}`);
|
|
85
87
|
return true;
|
|
86
88
|
} catch (err) {
|
|
87
|
-
|
|
89
|
+
if (String(err.message).includes('HTTP 404')) {
|
|
90
|
+
console.log(`[zcompress] ⚠ Release asset not found for v${VERSION}: ${binaryName}`);
|
|
91
|
+
console.log(`[zcompress] ⚠ Expected URL: ${url}`);
|
|
92
|
+
console.log('[zcompress] ⚠ This usually means the npm version was published before uploading binary assets to GitHub Releases.');
|
|
93
|
+
} else {
|
|
94
|
+
console.log(`[zcompress] ⚠ Could not download binary (${err.message}).`);
|
|
95
|
+
}
|
|
96
|
+
console.log('[zcompress] ℹ Build from source: cd zcompress && zig build -Doptimize=ReleaseFast');
|
|
88
97
|
// Clean up partial download
|
|
89
98
|
try { unlinkSync(destPath); } catch (_) { /* ignore */ }
|
|
90
99
|
return false;
|