vite-plugin-singlefile-compression 1.0.1 → 1.0.3
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 +32 -18
- package/dist/index.d.ts +10 -1
- package/dist/index.js +42 -19
- package/dist/template-assets.js +1 -1
- package/package.json +5 -6
- package/README-zh-CN.md +0 -93
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ Using `npm` to install
|
|
|
19
19
|
npm i vite-plugin-singlefile-compression
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
Then modify [vite.config.ts](
|
|
22
|
+
Then modify [vite.config.ts](test/vite.config.ts#L14)
|
|
23
23
|
|
|
24
24
|
```ts
|
|
25
25
|
// Import singleFileCompression
|
|
@@ -33,15 +33,41 @@ export default defineConfig({
|
|
|
33
33
|
// Add singleFileCompression
|
|
34
34
|
singleFileCompression(),
|
|
35
35
|
],
|
|
36
|
+
esbuild: {
|
|
37
|
+
// Remove license comments
|
|
38
|
+
legalComments: "none"
|
|
39
|
+
},
|
|
40
|
+
build: {
|
|
41
|
+
terserOptions: {
|
|
42
|
+
format: {
|
|
43
|
+
// Remove license comments
|
|
44
|
+
comments: false
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
target: 'esnext',
|
|
48
|
+
reportCompressedSize: false
|
|
49
|
+
},
|
|
36
50
|
```
|
|
37
51
|
|
|
38
|
-
Then modify [src/router/index.ts](
|
|
52
|
+
Then modify [src/router/index.ts](test/src/router/index.ts#L5) , change `createWebHistory` to `createWebHashHistory`
|
|
39
53
|
|
|
40
54
|
```ts
|
|
41
55
|
const router = createRouter({
|
|
42
56
|
history: createWebHashHistory(),
|
|
43
57
|
```
|
|
44
58
|
|
|
59
|
+
## Options
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
export interface Options {
|
|
63
|
+
/**
|
|
64
|
+
* https://github.com/terser/html-minifier-terser?tab=readme-ov-file#options-quick-reference
|
|
65
|
+
* @default defaultHtmlMinifierTerserOptions
|
|
66
|
+
*/
|
|
67
|
+
htmlMinifierTerser?: htmlMinifierOptions | true | false
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
45
71
|
## Effect
|
|
46
72
|
|
|
47
73
|
```
|
|
@@ -52,28 +78,16 @@ rendering chunks (1)...
|
|
|
52
78
|
vite-plugin-singlefile-compression building...
|
|
53
79
|
|
|
54
80
|
file:///D:/bddjr/Desktop/code/js/vite-plugin-singlefile-compression/test/dist/index.html
|
|
55
|
-
97.
|
|
81
|
+
97.52 KiB -> 50.98 KiB
|
|
56
82
|
|
|
57
83
|
Finish.
|
|
58
84
|
|
|
59
|
-
dist/index.html 52.
|
|
60
|
-
✓ built in
|
|
85
|
+
dist/index.html 52.19 kB
|
|
86
|
+
✓ built in 685ms
|
|
61
87
|
```
|
|
62
88
|
|
|
63
89
|
```html
|
|
64
|
-
<!DOCTYPE html>
|
|
65
|
-
<html>
|
|
66
|
-
<head>
|
|
67
|
-
<meta charset="UTF-8">
|
|
68
|
-
<link rel="icon" href="data:logo-_cUAdIX-.svg">
|
|
69
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
70
|
-
<title>Vite App</title>
|
|
71
|
-
<script type="module">fetch("data:application/gzip;base64,H4sI******AQA=").then(r=>r.blob()).then(b=>new Response(b.stream().pipeThrough(new DecompressionStream("gzip")),{headers:{"Content-Type":"text/javascript"}}).blob()).then(b=>import(b=URL.createObjectURL(b)).finally(()=>URL.revokeObjectURL(b)))</script>
|
|
72
|
-
</head>
|
|
73
|
-
<body>
|
|
74
|
-
<div id="app"></div>
|
|
75
|
-
</body>
|
|
76
|
-
</html>
|
|
90
|
+
<!DOCTYPE html><meta charset=UTF-8><link rel=icon href=data:logo-_cUAdIX-.svg><meta name=viewport content="width=device-width,initial-scale=1"><title>Vite App</title><script type=module>fetch("data:application/gzip;base64,H4sI********hAEA").then(r=>r.blob()).then(b=>new Response(b.stream().pipeThrough(new DecompressionStream("gzip")),{headers:{"Content-Type":"text/javascript"}}).blob()).then(b=>import(b=URL.createObjectURL(b)).finally(()=>URL.revokeObjectURL(b)))</script><div id=app></div>
|
|
77
91
|
```
|
|
78
92
|
|
|
79
93
|
## Clone
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
1
|
import { PluginOption } from "vite";
|
|
2
|
-
|
|
2
|
+
import { Options as htmlMinifierOptions } from 'html-minifier-terser';
|
|
3
|
+
export interface Options {
|
|
4
|
+
/**
|
|
5
|
+
* https://github.com/terser/html-minifier-terser?tab=readme-ov-file#options-quick-reference
|
|
6
|
+
* @default defaultHtmlMinifierTerserOptions
|
|
7
|
+
*/
|
|
8
|
+
htmlMinifierTerser?: htmlMinifierOptions | boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare const defaultHtmlMinifierTerserOptions: htmlMinifierOptions;
|
|
11
|
+
export declare function singleFileCompression(options?: Options): PluginOption;
|
|
3
12
|
export default singleFileCompression;
|
package/dist/index.js
CHANGED
|
@@ -1,28 +1,47 @@
|
|
|
1
1
|
import mime from 'mime';
|
|
2
2
|
import pc from "picocolors";
|
|
3
3
|
import svgToTinyDataUri from "mini-svg-data-uri";
|
|
4
|
+
import { minify as htmlMinify } from 'html-minifier-terser';
|
|
4
5
|
import zlib from 'zlib';
|
|
5
6
|
import path from 'path';
|
|
6
7
|
import fs from 'fs';
|
|
7
|
-
|
|
8
|
+
import { pathToFileURL } from "url";
|
|
9
|
+
export const defaultHtmlMinifierTerserOptions = {
|
|
10
|
+
removeAttributeQuotes: true,
|
|
11
|
+
removeComments: true,
|
|
12
|
+
collapseWhitespace: true,
|
|
13
|
+
removeOptionalTags: true,
|
|
14
|
+
removeRedundantAttributes: true,
|
|
15
|
+
minifyJS: false,
|
|
16
|
+
};
|
|
17
|
+
export function singleFileCompression(options) {
|
|
18
|
+
const htmlMinifierOptions = options?.htmlMinifierTerser == null || options.htmlMinifierTerser === true
|
|
19
|
+
? defaultHtmlMinifierTerserOptions
|
|
20
|
+
: options.htmlMinifierTerser;
|
|
8
21
|
return {
|
|
9
|
-
name: "
|
|
22
|
+
name: "singleFileCompression",
|
|
10
23
|
enforce: "post",
|
|
11
24
|
config: setConfig,
|
|
12
|
-
generateBundle,
|
|
25
|
+
generateBundle: (_, bundle) => generateBundle(bundle, htmlMinifierOptions),
|
|
13
26
|
};
|
|
14
27
|
}
|
|
15
28
|
export default singleFileCompression;
|
|
16
29
|
const template = fs.readFileSync(path.join(import.meta.dirname, "template.js")).toString();
|
|
17
30
|
const templateAssets = fs.readFileSync(path.join(import.meta.dirname, "template-assets.js")).toString();
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
31
|
+
const distURL = pathToFileURL(path.resolve("dist")) + "/";
|
|
32
|
+
function gzipToBase64(buf) {
|
|
33
|
+
return zlib.gzipSync(buf, {
|
|
34
|
+
level: zlib.constants.Z_BEST_COMPRESSION,
|
|
35
|
+
}).toString('base64');
|
|
36
|
+
}
|
|
37
|
+
function KiB(size) {
|
|
38
|
+
return `${Math.ceil(size / 10.24) / 100} KiB`;
|
|
39
|
+
}
|
|
21
40
|
function setConfig(config) {
|
|
22
41
|
config.base = './';
|
|
23
42
|
if (!config.build)
|
|
24
43
|
config.build = {};
|
|
25
|
-
config.build.assetsInlineLimit =
|
|
44
|
+
config.build.assetsInlineLimit = () => true;
|
|
26
45
|
config.build.chunkSizeWarningLimit = Infinity;
|
|
27
46
|
config.build.cssCodeSplit = false;
|
|
28
47
|
config.build.assetsDir = 'assets';
|
|
@@ -31,17 +50,10 @@ function setConfig(config) {
|
|
|
31
50
|
config.build.rollupOptions = {};
|
|
32
51
|
config.build.rollupOptions.output = { inlineDynamicImports: true };
|
|
33
52
|
}
|
|
34
|
-
function
|
|
35
|
-
return zlib.gzipSync(buf, {
|
|
36
|
-
level: zlib.constants.Z_BEST_COMPRESSION,
|
|
37
|
-
}).toString('base64');
|
|
38
|
-
}
|
|
39
|
-
function KiB(size) {
|
|
40
|
-
return `${Math.ceil(size / 10.24) / 100} KiB`;
|
|
41
|
-
}
|
|
42
|
-
function generateBundle(_, bundle) {
|
|
53
|
+
async function generateBundle(bundle, htmlMinifierOptions) {
|
|
43
54
|
console.log(pc.cyan('\n\nvite-plugin-singlefile-compression ') + pc.green('building...'));
|
|
44
55
|
const globalDel = new Set();
|
|
56
|
+
const globalDoNotDel = new Set();
|
|
45
57
|
for (const htmlFileName of Object.keys(bundle)) {
|
|
46
58
|
// key format:
|
|
47
59
|
// index.html
|
|
@@ -54,7 +66,7 @@ function generateBundle(_, bundle) {
|
|
|
54
66
|
let newHtml = htmlChunk.source;
|
|
55
67
|
let oldSize = newHtml.length;
|
|
56
68
|
const thisDel = new Set();
|
|
57
|
-
// Fix async import
|
|
69
|
+
// Fix async import, fix new URL
|
|
58
70
|
const newJSCode = ["self.__VITE_PRELOAD__=void 0"];
|
|
59
71
|
// get css tag
|
|
60
72
|
newHtml = newHtml.replace(/\s*<link rel="stylesheet"[^>]* href="\.\/(assets\/[^"]+)"[^>]*>/, (match, name) => {
|
|
@@ -100,6 +112,14 @@ function generateBundle(_, bundle) {
|
|
|
100
112
|
thisDel.add(name);
|
|
101
113
|
const js = bundle[name];
|
|
102
114
|
oldSize += js.code.length;
|
|
115
|
+
// fix new URL
|
|
116
|
+
newJSCode.push(`import.meta.url=location.origin+location.pathname.replace(/[^/]*$/,"${name}")`);
|
|
117
|
+
for (const name in assets) {
|
|
118
|
+
// name: logo-XXXXXXXX.svg
|
|
119
|
+
if (js.code.includes(name))
|
|
120
|
+
globalDoNotDel.add("assets/" + name);
|
|
121
|
+
}
|
|
122
|
+
// add script
|
|
103
123
|
newJSCode.push(js.code.replace(/;?\n?$/, ''));
|
|
104
124
|
// gzip
|
|
105
125
|
return '<script type="module">'
|
|
@@ -108,10 +128,12 @@ function generateBundle(_, bundle) {
|
|
|
108
128
|
});
|
|
109
129
|
if (!ok)
|
|
110
130
|
continue;
|
|
131
|
+
if (htmlMinifierOptions)
|
|
132
|
+
newHtml = await htmlMinify(newHtml, htmlMinifierOptions);
|
|
111
133
|
// finish
|
|
112
134
|
htmlChunk.source = newHtml;
|
|
113
135
|
console.log("\n"
|
|
114
|
-
+ " " + pc.underline(pc.cyan(
|
|
136
|
+
+ " " + pc.underline(pc.cyan(distURL) + pc.greenBright(htmlFileName)) + '\n'
|
|
115
137
|
+ " " + pc.gray(KiB(oldSize) + " -> ") + pc.cyanBright(KiB(newHtml.length)) + '\n');
|
|
116
138
|
// delete assets
|
|
117
139
|
for (const name of thisDel) {
|
|
@@ -120,7 +142,8 @@ function generateBundle(_, bundle) {
|
|
|
120
142
|
}
|
|
121
143
|
// delete inlined assets
|
|
122
144
|
for (const name of globalDel) {
|
|
123
|
-
|
|
145
|
+
if (!globalDoNotDel.has(name))
|
|
146
|
+
delete bundle[name];
|
|
124
147
|
}
|
|
125
148
|
console.log(pc.green('Finish.\n'));
|
|
126
149
|
}
|
package/dist/template-assets.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{const a={"":""};for(const n in a){for(const e of document.querySelectorAll(`[src="data:${n}"]`))
|
|
1
|
+
{const a={"":""};for(const n in a){for(const e of document.querySelectorAll(`[src="data:${n}"]`))e.src=a[n];for(const e of document.querySelectorAll(`[href="data:${n}"]`))e.href=a[n]}}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-plugin-singlefile-compression",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"typings": "dist/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -8,8 +8,7 @@
|
|
|
8
8
|
"package.json",
|
|
9
9
|
"package-lock.json",
|
|
10
10
|
"LICENSE.txt",
|
|
11
|
-
"README.md"
|
|
12
|
-
"README-zh-CN.md"
|
|
11
|
+
"README.md"
|
|
13
12
|
],
|
|
14
13
|
"type": "module",
|
|
15
14
|
"scripts": {
|
|
@@ -39,15 +38,15 @@
|
|
|
39
38
|
"css"
|
|
40
39
|
],
|
|
41
40
|
"dependencies": {
|
|
41
|
+
"@types/html-minifier-terser": "^7.0.2",
|
|
42
|
+
"@types/node": "^22.9.3",
|
|
42
43
|
"esbuild": "^0.24.0",
|
|
44
|
+
"html-minifier-terser": "^7.2.0",
|
|
43
45
|
"mime": "^4.0.4",
|
|
44
46
|
"mini-svg-data-uri": "^1.4.4",
|
|
45
47
|
"picocolors": "^1.1.1",
|
|
46
48
|
"rimraf": "^6.0.1",
|
|
47
49
|
"typescript": "^5.7.2",
|
|
48
50
|
"vite": "^5.4.11"
|
|
49
|
-
},
|
|
50
|
-
"devDependencies": {
|
|
51
|
-
"@types/node": "^22.9.3"
|
|
52
51
|
}
|
|
53
52
|
}
|
package/README-zh-CN.md
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
# vite plugin singlefile compression
|
|
2
|
-
|
|
3
|
-
该插件使用 gzip 将所有 JavaScript、CSS、图片等资源压缩后,嵌入到 `dist/index.html` ,方便作为单个 HTML 文件分享。
|
|
4
|
-
|
|
5
|
-
接收方可以直接使用浏览器打开,无需手动解压文件。
|
|
6
|
-
|
|
7
|
-
改编自 [vite-plugin-singlefile](https://www.npmjs.com/package/vite-plugin-singlefile)
|
|
8
|
-
|
|
9
|
-
### README Language
|
|
10
|
-
|
|
11
|
-
> [English](README.md)
|
|
12
|
-
> 简体中文
|
|
13
|
-
|
|
14
|
-
## 安装
|
|
15
|
-
|
|
16
|
-
使用 `npm` 安装
|
|
17
|
-
|
|
18
|
-
```
|
|
19
|
-
npm i vite-plugin-singlefile-compression
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
然后修改 [vite.config.ts](https://github.com/bddjr/vite-plugin-singlefile-compression/blob/main/test/vite.config.ts#L14)
|
|
23
|
-
|
|
24
|
-
```ts
|
|
25
|
-
// 导入 singleFileCompression
|
|
26
|
-
import singleFileCompression from 'vite-plugin-singlefile-compression'
|
|
27
|
-
|
|
28
|
-
// https://vite.dev/config/
|
|
29
|
-
export default defineConfig({
|
|
30
|
-
plugins: [
|
|
31
|
-
vue(),
|
|
32
|
-
vueDevTools(),
|
|
33
|
-
// 添加 singleFileCompression
|
|
34
|
-
singleFileCompression(),
|
|
35
|
-
],
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
然后修改 [src/router/index.ts](https://github.com/bddjr/vite-plugin-singlefile-compression/blob/main/test/src/router/index.ts#L5) ,将 `createWebHistory` 改为 `createWebHashHistory`
|
|
39
|
-
|
|
40
|
-
```ts
|
|
41
|
-
const router = createRouter({
|
|
42
|
-
history: createWebHashHistory(),
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
## 效果
|
|
46
|
-
|
|
47
|
-
```
|
|
48
|
-
vite v5.4.11 building for production...
|
|
49
|
-
✓ 45 modules transformed.
|
|
50
|
-
rendering chunks (1)...
|
|
51
|
-
|
|
52
|
-
vite-plugin-singlefile-compression building...
|
|
53
|
-
|
|
54
|
-
file:///D:/bddjr/Desktop/code/js/vite-plugin-singlefile-compression/test/dist/index.html
|
|
55
|
-
97.2 KiB -> 50.91 KiB
|
|
56
|
-
|
|
57
|
-
Finish.
|
|
58
|
-
|
|
59
|
-
dist/index.html 52.13 kB
|
|
60
|
-
✓ built in 778ms
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
```html
|
|
64
|
-
<!DOCTYPE html>
|
|
65
|
-
<html>
|
|
66
|
-
<head>
|
|
67
|
-
<meta charset="UTF-8">
|
|
68
|
-
<link rel="icon" href="data:logo-_cUAdIX-.svg">
|
|
69
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
70
|
-
<title>Vite App</title>
|
|
71
|
-
<script type="module">fetch("data:application/gzip;base64,H4sI******AQA=").then(r=>r.blob()).then(b=>new Response(b.stream().pipeThrough(new DecompressionStream("gzip")),{headers:{"Content-Type":"text/javascript"}}).blob()).then(b=>import(b=URL.createObjectURL(b)).finally(()=>URL.revokeObjectURL(b)))</script>
|
|
72
|
-
</head>
|
|
73
|
-
<body>
|
|
74
|
-
<div id="app"></div>
|
|
75
|
-
</body>
|
|
76
|
-
</html>
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
## Clone
|
|
80
|
-
|
|
81
|
-
```
|
|
82
|
-
git clone https://github.com/bddjr/vite-plugin-singlefile-compression
|
|
83
|
-
cd vite-plugin-singlefile-compression
|
|
84
|
-
npm i
|
|
85
|
-
cd test
|
|
86
|
-
npm i
|
|
87
|
-
cd ..
|
|
88
|
-
npm run build
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
## License
|
|
92
|
-
|
|
93
|
-
[MIT](LICENSE.txt)
|