zcompress-vite-plugin 0.1.3 → 0.1.5
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 +78 -29
- package/index.d.ts +2 -0
- package/index.js +24 -8
- package/install.js +93 -41
- 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,42 @@ 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` 失败 |
|
|
53
|
+
|
|
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
|
+
```
|
|
63
|
+
|
|
64
|
+
说明 npm 版本已发布,但对应 GitHub Release 还没上传该平台二进制。
|
|
62
65
|
|
|
63
|
-
|
|
66
|
+
解决方式:
|
|
64
67
|
|
|
65
|
-
|
|
68
|
+
1. 手动安装 CLI(上面安装表)
|
|
69
|
+
2. 在插件里指定 `binaryPath`
|
|
70
|
+
|
|
71
|
+
```js
|
|
72
|
+
zcompress({
|
|
73
|
+
binaryPath: '/absolute/path/to/zcompress',
|
|
74
|
+
})
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
也可用环境变量(不改代码):
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
export ZCOMPRESS_BINARY=/absolute/path/to/zcompress
|
|
81
|
+
npm run build
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
默认 `failOnError: true`,失败会直接中断构建(不会静默跳过压缩)。
|
|
66
85
|
|
|
67
86
|
## License
|
|
68
87
|
|
|
@@ -80,11 +99,16 @@ High-performance multi-threaded asset compression for Vite production builds. 5-
|
|
|
80
99
|
npm install zcompress-vite-plugin --save-dev
|
|
81
100
|
```
|
|
82
101
|
|
|
83
|
-
The
|
|
102
|
+
The package auto-downloads a platform binary. If that fails, install/build manually:
|
|
84
103
|
|
|
85
104
|
```bash
|
|
86
|
-
|
|
87
|
-
|
|
105
|
+
# macOS
|
|
106
|
+
brew install zstd brotli
|
|
107
|
+
zig build -Doptimize=ReleaseFast
|
|
108
|
+
|
|
109
|
+
# Linux
|
|
110
|
+
sudo apt-get install -y libzstd-dev libbrotli-dev
|
|
111
|
+
zig build -Doptimize=ReleaseFast
|
|
88
112
|
```
|
|
89
113
|
|
|
90
114
|
## Usage
|
|
@@ -97,7 +121,7 @@ export default {
|
|
|
97
121
|
plugins: [
|
|
98
122
|
zcompress({
|
|
99
123
|
algo: 'brotli', // 'gzip' | 'zstd' | 'brotli'
|
|
100
|
-
level:
|
|
124
|
+
level: 9, // 1-9
|
|
101
125
|
threads: 0, // 0 = auto
|
|
102
126
|
cache: true, // skip unchanged files
|
|
103
127
|
})
|
|
@@ -105,15 +129,7 @@ export default {
|
|
|
105
129
|
};
|
|
106
130
|
```
|
|
107
131
|
|
|
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
|
-
```
|
|
132
|
+
After `vite build`, compressed assets are written to `dist-compressed/`.
|
|
117
133
|
|
|
118
134
|
## Options
|
|
119
135
|
|
|
@@ -127,6 +143,39 @@ dist-compressed/
|
|
|
127
143
|
| `include` | `string[]` | `[]` | Extra extensions to compress |
|
|
128
144
|
| `exclude` | `string[]` | `[]` | Extensions to skip |
|
|
129
145
|
| `binaryPath` | `string` | auto | Override binary path |
|
|
146
|
+
| `failOnError` | `boolean` | `true` | Fail `vite build` when compression fails |
|
|
147
|
+
|
|
148
|
+
## Troubleshooting
|
|
149
|
+
|
|
150
|
+
### `postinstall` binary download returns 404
|
|
151
|
+
|
|
152
|
+
If you see:
|
|
153
|
+
|
|
154
|
+
```txt
|
|
155
|
+
https://github.com/luochuan2008/zcompress/releases/download/vX.Y.Z/zcompress-macos-arm64 → 404
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
that npm version exists, but the matching GitHub Release binary asset is missing.
|
|
159
|
+
|
|
160
|
+
Workarounds:
|
|
161
|
+
|
|
162
|
+
1. Install/build the CLI manually
|
|
163
|
+
2. Set `binaryPath` explicitly
|
|
164
|
+
|
|
165
|
+
```js
|
|
166
|
+
zcompress({
|
|
167
|
+
binaryPath: '/absolute/path/to/zcompress',
|
|
168
|
+
})
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Or use env var (no code change):
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
export ZCOMPRESS_BINARY=/absolute/path/to/zcompress
|
|
175
|
+
npm run build
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Default `failOnError: true` ensures builds fail loudly (instead of silently skipping compression).
|
|
130
179
|
|
|
131
180
|
## License
|
|
132
181
|
|
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
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* };
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import {
|
|
18
|
+
import { execFileSync } from 'node:child_process';
|
|
19
19
|
import { existsSync, statSync, readdirSync } from 'node:fs';
|
|
20
20
|
import { join, dirname, isAbsolute } from 'node:path';
|
|
21
21
|
import { fileURLToPath } from 'node:url';
|
|
@@ -32,6 +32,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
32
32
|
* @property {string[]} [include] - Extra file extensions to include
|
|
33
33
|
* @property {string[]} [exclude] - File extensions to exclude
|
|
34
34
|
* @property {string} [binaryPath] - Path to zcompress binary
|
|
35
|
+
* @property {boolean} [failOnError=true] - Fail Vite build if compression fails
|
|
35
36
|
*/
|
|
36
37
|
|
|
37
38
|
/** @type {ZCompressOptions} */
|
|
@@ -43,18 +44,23 @@ const DEFAULT_OPTIONS = {
|
|
|
43
44
|
cache: false,
|
|
44
45
|
include: [],
|
|
45
46
|
exclude: [],
|
|
47
|
+
failOnError: true,
|
|
46
48
|
};
|
|
47
49
|
|
|
48
50
|
/**
|
|
49
51
|
* Find the zcompress binary.
|
|
50
|
-
* Order: 1) downloaded by postinstall
|
|
52
|
+
* Order: 1) ZCOMPRESS_BINARY env 2) downloaded by postinstall 3) local zig build 4) PATH
|
|
51
53
|
*/
|
|
52
54
|
function findBinary() {
|
|
53
|
-
// 1)
|
|
55
|
+
// 1) explicit env override
|
|
56
|
+
const envBinary = process.env.ZCOMPRESS_BINARY;
|
|
57
|
+
if (envBinary) return envBinary;
|
|
58
|
+
|
|
59
|
+
// 2) postinstall-downloaded binary inside package
|
|
54
60
|
const packagedBin = join(__dirname, 'bin', process.platform === 'win32' ? 'zcompress.exe' : 'zcompress');
|
|
55
61
|
if (existsSync(packagedBin)) return packagedBin;
|
|
56
62
|
|
|
57
|
-
//
|
|
63
|
+
// 3) local zig build
|
|
58
64
|
const zigPaths = [
|
|
59
65
|
join(process.cwd(), 'zig-out', 'bin', 'zcompress'),
|
|
60
66
|
join(process.cwd(), 'zig-out', 'bin', 'zcompress.exe'),
|
|
@@ -63,7 +69,7 @@ function findBinary() {
|
|
|
63
69
|
if (existsSync(p)) return p;
|
|
64
70
|
}
|
|
65
71
|
|
|
66
|
-
//
|
|
72
|
+
// 4) PATH
|
|
67
73
|
return 'zcompress';
|
|
68
74
|
}
|
|
69
75
|
|
|
@@ -130,7 +136,7 @@ export default function zcompressPlugin(userOptions = {}) {
|
|
|
130
136
|
const startTime = Date.now();
|
|
131
137
|
|
|
132
138
|
try {
|
|
133
|
-
|
|
139
|
+
execFileSync(binary, args, { stdio: 'inherit' });
|
|
134
140
|
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
135
141
|
|
|
136
142
|
const srcSize = getDirSize(outDir);
|
|
@@ -141,8 +147,18 @@ export default function zcompressPlugin(userOptions = {}) {
|
|
|
141
147
|
|
|
142
148
|
console.log(`[zcompress] ✅ Compressed ${formatSize(srcSize)} → ${formatSize(destSize)} (saved ${savedPct}%) in ${elapsed}s`);
|
|
143
149
|
} catch (err) {
|
|
144
|
-
|
|
145
|
-
|
|
150
|
+
const message = [
|
|
151
|
+
`[zcompress] ❌ Compression failed: ${err.message}`,
|
|
152
|
+
`[zcompress] Binary used: ${binary}`,
|
|
153
|
+
'[zcompress] If this is a 404 download issue, GitHub Release may be missing prebuilt binaries for this version.',
|
|
154
|
+
'[zcompress] Workaround: install CLI manually (`zig build -Doptimize=ReleaseFast`) and set `binaryPath` in plugin options.',
|
|
155
|
+
].join('\n');
|
|
156
|
+
|
|
157
|
+
if (options.failOnError !== false) {
|
|
158
|
+
throw new Error(message);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
console.error(message);
|
|
146
162
|
}
|
|
147
163
|
},
|
|
148
164
|
};
|
package/install.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Postinstall script — downloads the prebuilt zcompress binary from GitHub Releases.
|
|
5
|
-
* Falls back gracefully if
|
|
5
|
+
* Falls back gracefully if binary can't be fetched.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { createWriteStream, existsSync, chmodSync, mkdirSync, unlinkSync, readFileSync } from 'node:fs';
|
|
@@ -20,10 +20,10 @@ const pkgJson = JSON.parse(readFileSync(join(__dirname, 'package.json'), 'utf8')
|
|
|
20
20
|
const VERSION = pkgJson.version;
|
|
21
21
|
|
|
22
22
|
const PLATFORM_MAP = {
|
|
23
|
-
'darwin-arm64':
|
|
24
|
-
'darwin-x64':
|
|
25
|
-
'linux-x64':
|
|
26
|
-
'win32-x64':
|
|
23
|
+
'darwin-arm64': 'zcompress-macos-arm64',
|
|
24
|
+
'darwin-x64': 'zcompress-macos-x64',
|
|
25
|
+
'linux-x64': 'zcompress-linux-x64',
|
|
26
|
+
'win32-x64': 'zcompress-windows-x64.exe',
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
function getPlatformKey() {
|
|
@@ -33,8 +33,54 @@ function getPlatformKey() {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
function getBinaryName() {
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
return PLATFORM_MAP[getPlatformKey()] || null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function request(url) {
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
get(url, {
|
|
42
|
+
headers: {
|
|
43
|
+
'User-Agent': 'zcompress-vite-plugin-install',
|
|
44
|
+
Accept: 'application/vnd.github+json',
|
|
45
|
+
},
|
|
46
|
+
}, (res) => resolve(res)).on('error', reject);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function fetchJson(url) {
|
|
51
|
+
const res = await request(url);
|
|
52
|
+
if (res.statusCode !== 200) {
|
|
53
|
+
throw new Error(`HTTP ${res.statusCode}`);
|
|
54
|
+
}
|
|
55
|
+
const chunks = [];
|
|
56
|
+
for await (const chunk of res) chunks.push(chunk);
|
|
57
|
+
return JSON.parse(Buffer.concat(chunks).toString('utf8'));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function downloadToFile(url, destPath) {
|
|
61
|
+
const res = await request(url);
|
|
62
|
+
|
|
63
|
+
// Follow redirect (GitHub release assets usually 302)
|
|
64
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
65
|
+
return downloadToFile(res.headers.location, destPath);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (res.statusCode !== 200) {
|
|
69
|
+
throw new Error(`HTTP ${res.statusCode}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const file = createWriteStream(destPath);
|
|
73
|
+
await pipeline(res, file);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function resolveFallbackAssetUrl(binaryName) {
|
|
77
|
+
// Fallback strategy: use latest release asset for this platform when current version asset is missing.
|
|
78
|
+
const latest = await fetchJson(`https://api.github.com/repos/${REPO}/releases/latest`);
|
|
79
|
+
const asset = (latest.assets || []).find((a) => a.name === binaryName);
|
|
80
|
+
if (!asset?.browser_download_url) {
|
|
81
|
+
throw new Error('Latest release has no matching asset');
|
|
82
|
+
}
|
|
83
|
+
return asset.browser_download_url;
|
|
38
84
|
}
|
|
39
85
|
|
|
40
86
|
async function downloadBinary() {
|
|
@@ -52,49 +98,55 @@ async function downloadBinary() {
|
|
|
52
98
|
return true;
|
|
53
99
|
}
|
|
54
100
|
|
|
55
|
-
|
|
56
|
-
console.log(`[zcompress] ↓ Downloading ${url} ...`);
|
|
101
|
+
mkdirSync(BIN_DIR, { recursive: true });
|
|
57
102
|
|
|
103
|
+
const versionedUrl = `https://github.com/${REPO}/releases/download/v${VERSION}/${binaryName}`;
|
|
104
|
+
const tried = [];
|
|
105
|
+
|
|
106
|
+
// Attempt 1: exact npm version
|
|
58
107
|
try {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
await
|
|
62
|
-
const file = createWriteStream(destPath);
|
|
63
|
-
get(url, (response) => {
|
|
64
|
-
// Follow redirects
|
|
65
|
-
if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
|
|
66
|
-
get(response.headers.location, (redirectRes) => {
|
|
67
|
-
pipeline(redirectRes, file).then(resolve).catch(reject);
|
|
68
|
-
}).on('error', reject);
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
if (response.statusCode !== 200) {
|
|
72
|
-
file.close();
|
|
73
|
-
unlinkSync(destPath);
|
|
74
|
-
reject(new Error(`HTTP ${response.statusCode}`));
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
pipeline(response, file).then(resolve).catch(reject);
|
|
78
|
-
}).on('error', reject);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
// Make executable on Unix
|
|
82
|
-
if (process.platform !== 'win32') {
|
|
83
|
-
chmodSync(destPath, 0o755);
|
|
84
|
-
}
|
|
108
|
+
tried.push(versionedUrl);
|
|
109
|
+
console.log(`[zcompress] ↓ Downloading ${versionedUrl} ...`);
|
|
110
|
+
await downloadToFile(versionedUrl, destPath);
|
|
85
111
|
|
|
112
|
+
if (process.platform !== 'win32') chmodSync(destPath, 0o755);
|
|
86
113
|
console.log(`[zcompress] ✓ Binary installed: ${destPath}`);
|
|
87
114
|
return true;
|
|
88
115
|
} catch (err) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
116
|
+
// cleanup partial
|
|
117
|
+
try { unlinkSync(destPath); } catch {}
|
|
118
|
+
|
|
119
|
+
// If 404 or similar, try latest release as fallback
|
|
120
|
+
const msg = String(err?.message || err);
|
|
121
|
+
if (!msg.includes('404')) {
|
|
122
|
+
console.log(`[zcompress] ⚠ Download failed (${msg}).`);
|
|
123
|
+
} else {
|
|
124
|
+
console.log(`[zcompress] ⚠ Asset missing for v${VERSION}: ${binaryName}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Attempt 2: latest release fallback
|
|
129
|
+
try {
|
|
130
|
+
const latestAssetUrl = await resolveFallbackAssetUrl(binaryName);
|
|
131
|
+
tried.push(latestAssetUrl);
|
|
132
|
+
console.log(`[zcompress] ↓ Falling back to latest release asset: ${latestAssetUrl}`);
|
|
133
|
+
await downloadToFile(latestAssetUrl, destPath);
|
|
134
|
+
|
|
135
|
+
if (process.platform !== 'win32') chmodSync(destPath, 0o755);
|
|
136
|
+
console.log(`[zcompress] ✓ Binary installed from latest release: ${destPath}`);
|
|
137
|
+
return true;
|
|
138
|
+
} catch (err) {
|
|
139
|
+
try { unlinkSync(destPath); } catch {}
|
|
140
|
+
console.log(`[zcompress] ⚠ Fallback download failed (${err.message}).`);
|
|
141
|
+
console.log('[zcompress] Tried URLs:');
|
|
142
|
+
for (const u of tried) console.log(` - ${u}`);
|
|
143
|
+
console.log('[zcompress] ℹ Build from source: cd zcompress && zig build -Doptimize=ReleaseFast');
|
|
92
144
|
return false;
|
|
93
145
|
}
|
|
94
146
|
}
|
|
95
147
|
|
|
96
148
|
// Run download, but don't fail install if it doesn't work
|
|
97
|
-
downloadBinary().catch(() => {
|
|
98
|
-
console.log(
|
|
99
|
-
console.log('[zcompress] ℹ Install it
|
|
149
|
+
downloadBinary().catch((err) => {
|
|
150
|
+
console.log(`[zcompress] ⚠ Binary download skipped (${err.message}).`);
|
|
151
|
+
console.log('[zcompress] ℹ Install it manually and set plugin option `binaryPath`.');
|
|
100
152
|
});
|