vite-plugin-singlefile-compression 2.2.1 → 2.2.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 +5 -5
- package/dist/index.d.ts +16 -16
- package/dist/index.js +50 -47
- package/package.json +4 -6
package/README.md
CHANGED
|
@@ -152,21 +152,21 @@ type: `boolean`
|
|
|
152
152
|
Preview: https://bddjr.github.io/vite-plugin-singlefile-compression/
|
|
153
153
|
|
|
154
154
|
```
|
|
155
|
-
vite v8.0.
|
|
155
|
+
vite v8.0.5 building client environment for production...
|
|
156
156
|
✓ 42 modules transformed.
|
|
157
157
|
rendering chunks (1)...
|
|
158
158
|
|
|
159
|
-
vite-plugin-singlefile-compression 2.2.
|
|
159
|
+
vite-plugin-singlefile-compression 2.2.3 deflate-raw base128-ascii
|
|
160
160
|
|
|
161
161
|
file:///D:/code/js/vite-plugin-singlefile-compression/test/dist/index.html
|
|
162
|
-
122.
|
|
162
|
+
122.750 kB -> 50.221 kB
|
|
163
163
|
|
|
164
164
|
Finish.
|
|
165
165
|
|
|
166
166
|
computing gzip size...
|
|
167
|
-
dist/index.html 50.22 kB │ gzip: 43.
|
|
167
|
+
dist/index.html 50.22 kB │ gzip: 43.79 kB
|
|
168
168
|
|
|
169
|
-
✓ built in
|
|
169
|
+
✓ built in 275ms
|
|
170
170
|
```
|
|
171
171
|
|
|
172
172
|
## Clone
|
package/dist/index.d.ts
CHANGED
|
@@ -2,27 +2,27 @@ import { Options as HtmlMinifierOptions } from "html-minifier-terser";
|
|
|
2
2
|
import zlib from "zlib";
|
|
3
3
|
import { PluginOption } from "vite";
|
|
4
4
|
|
|
5
|
-
//#region
|
|
5
|
+
//#region src/compress.d.ts
|
|
6
6
|
declare const compressors: {
|
|
7
|
-
"deflate-raw"(buf: zlib.InputType)
|
|
8
|
-
deflate(buf: zlib.InputType)
|
|
9
|
-
gzip(buf: zlib.InputType)
|
|
10
|
-
brotli: typeof zlib.brotliCompressSync;
|
|
11
|
-
zstd: (buf: zlib.InputType) =>
|
|
7
|
+
readonly "deflate-raw": (buf: zlib.InputType) => Buffer;
|
|
8
|
+
readonly deflate: (buf: zlib.InputType) => Buffer;
|
|
9
|
+
readonly gzip: (buf: zlib.InputType) => Buffer;
|
|
10
|
+
readonly brotli: typeof zlib.brotliCompressSync;
|
|
11
|
+
readonly zstd: (buf: zlib.InputType) => Buffer;
|
|
12
|
+
};
|
|
13
|
+
declare const compressFormatAlias: {
|
|
14
|
+
readonly deflateRaw: "deflate-raw";
|
|
15
|
+
readonly gz: "gzip";
|
|
16
|
+
readonly br: "brotli";
|
|
17
|
+
readonly brotliCompress: "brotli";
|
|
18
|
+
readonly zstandard: "zstd";
|
|
19
|
+
readonly zst: "zstd";
|
|
12
20
|
};
|
|
13
|
-
declare const compressFormatAlias: Readonly<{
|
|
14
|
-
deflateRaw: "deflate-raw";
|
|
15
|
-
gz: "gzip";
|
|
16
|
-
br: "brotli";
|
|
17
|
-
brotliCompress: "brotli";
|
|
18
|
-
zstandard: "zstd";
|
|
19
|
-
zst: "zstd";
|
|
20
|
-
}>;
|
|
21
21
|
type Compressor = ((buf: zlib.InputType) => (Buffer | Uint8Array | Promise<Buffer | Uint8Array>));
|
|
22
22
|
type CompressFormat = keyof typeof compressors | CompressionFormat;
|
|
23
23
|
type CompressFormatAlias = keyof typeof compressFormatAlias;
|
|
24
24
|
//#endregion
|
|
25
|
-
//#region
|
|
25
|
+
//#region src/options.d.ts
|
|
26
26
|
interface Options {
|
|
27
27
|
/**
|
|
28
28
|
* Rename index.html
|
|
@@ -87,7 +87,7 @@ interface Options {
|
|
|
87
87
|
}
|
|
88
88
|
declare const defaultHtmlMinifierTerserOptions: HtmlMinifierOptions;
|
|
89
89
|
//#endregion
|
|
90
|
-
//#region
|
|
90
|
+
//#region src/index.d.ts
|
|
91
91
|
declare function singleFileCompression(opt?: Options): PluginOption;
|
|
92
92
|
//#endregion
|
|
93
93
|
export { CompressFormat, CompressFormatAlias, Compressor, HtmlMinifierOptions, Options, singleFileCompression as default, singleFileCompression, defaultHtmlMinifierTerserOptions };
|
package/dist/index.js
CHANGED
|
@@ -8,10 +8,10 @@ import base128 from "base128-ascii";
|
|
|
8
8
|
import zlib from "zlib";
|
|
9
9
|
import svgToTinyDataUri from "mini-svg-data-uri";
|
|
10
10
|
import mime from "mime";
|
|
11
|
-
//#region
|
|
12
|
-
var version = "2.2.
|
|
11
|
+
//#region package.json
|
|
12
|
+
var version = "2.2.3";
|
|
13
13
|
//#endregion
|
|
14
|
-
//#region
|
|
14
|
+
//#region src/compress.ts
|
|
15
15
|
const compressors = {
|
|
16
16
|
"deflate-raw"(buf) {
|
|
17
17
|
return zlib.deflateRawSync(buf, { level: zlib.constants.Z_BEST_COMPRESSION });
|
|
@@ -25,14 +25,14 @@ const compressors = {
|
|
|
25
25
|
brotli: zlib.brotliCompressSync,
|
|
26
26
|
zstd: zlib.zstdCompressSync && ((buf) => zlib.zstdCompressSync(buf, { params: { [zlib.constants.ZSTD_c_compressionLevel]: 19 } }))
|
|
27
27
|
};
|
|
28
|
-
const compressFormatAlias =
|
|
28
|
+
const compressFormatAlias = {
|
|
29
29
|
deflateRaw: "deflate-raw",
|
|
30
30
|
gz: "gzip",
|
|
31
31
|
br: "brotli",
|
|
32
32
|
brotliCompress: "brotli",
|
|
33
33
|
zstandard: "zstd",
|
|
34
34
|
zst: "zstd"
|
|
35
|
-
}
|
|
35
|
+
};
|
|
36
36
|
function switchCompressor(format) {
|
|
37
37
|
if (compressors.hasOwnProperty(format)) {
|
|
38
38
|
const f = compressors[format];
|
|
@@ -64,62 +64,59 @@ async function compress(format, buf, useBase128, compressor) {
|
|
|
64
64
|
return Buffer.from(outBuf).toString("base64");
|
|
65
65
|
}
|
|
66
66
|
//#endregion
|
|
67
|
-
//#region _dist/templateRaw.
|
|
68
|
-
var raw = {
|
|
69
|
-
"assets": "{let e={\"\":\"\"};for(let o in e)for(let s of document.querySelectorAll(`[src=\"data:${o}\"]`))s.src=e[o]}",
|
|
70
|
-
"base128": "var a=\"<script>\",c=a.length,r=new Uint8Array(c/8*7),p=0,n=0,e,t=o=>(e=a.charCodeAt(p++))>127?e=0:e,i=Response;for(;p<c;)r[n++]=t()<<1|t()>>6,r[n++]=e<<2|t()>>5,r[n++]=e<<3|t()>>4,r[n++]=e<<4|t()>>3,r[n++]=e<<5|t()>>2,r[n++]=e<<6|t()>>1,r[n++]=e<<7|t();new i(new i(r).body.pipeThrough(new DecompressionStream(\"<format>\")),{headers:{\"Content-Type\":\"text/javascript\"}}).blob().then(o=>(import(o=URL.createObjectURL(o)),URL.revokeObjectURL(o)))",
|
|
71
|
-
"base64": "fetch(\"data:;base64,<script>\").then(e=>new Response(e.body.pipeThrough(new DecompressionStream(\"<format>\")),{headers:{\"Content-Type\":\"text/javascript\"}}).blob()).then(e=>(import(e=URL.createObjectURL(e)),URL.revokeObjectURL(e)))",
|
|
72
|
-
"css": "document.head.appendChild(document.createElement(\"style\")).innerHTML=\"<style>\"",
|
|
73
|
-
"icon": "document.querySelector(\"link[rel=icon]\").href=\"<icon>\"",
|
|
74
|
-
"importmeta": "{let e=import.meta,r=e.resolve,l=e.url=new URL(\"<path>\",location).href;e.resolve=function(t){return/^\\.{0,2}\\//.test(t)?new URL(t,l).href:r.apply(this,arguments)}}"
|
|
75
|
-
};
|
|
76
|
-
//#endregion
|
|
77
|
-
//#region _dist/getTemplate.js
|
|
78
|
-
function split2(str, separator) {
|
|
79
|
-
const s = str.split(separator);
|
|
80
|
-
if (s.length !== 2) throw Error("s.length!==2");
|
|
81
|
-
return s;
|
|
82
|
-
}
|
|
67
|
+
//#region _dist/templateRaw.ts
|
|
83
68
|
const files = {
|
|
84
|
-
|
|
85
|
-
base128:
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
69
|
+
"assets": ["{let e=", ";for(let l in e)for(let r of document.querySelectorAll(`[src=\"data:${l}\"]`))r.src=e[l]}"],
|
|
70
|
+
"base128": [
|
|
71
|
+
"for(var e,t=",
|
|
72
|
+
",r=t.length,n=new Uint8Array(r/8*7),o=0,a=0,_=r=>(e=t.charCodeAt(o++))>127?e=0:e,p=Response;o<r;)n[a++]=_()<<1|_()>>6,n[a++]=e<<2|_()>>5,n[a++]=e<<3|_()>>4,n[a++]=e<<4|_()>>3,n[a++]=e<<5|_()>>2,n[a++]=e<<6|_()>>1,n[a++]=e<<7|_();new p(new p(n).body.pipeThrough(new DecompressionStream(",
|
|
73
|
+
")),{headers:{\"Content-Type\":\"text/javascript\"}}).blob().then(e=>(import(e=URL.createObjectURL(e)),URL.revokeObjectURL(e)))"
|
|
74
|
+
],
|
|
75
|
+
"base64": [
|
|
76
|
+
"fetch(\"data:;base64,",
|
|
77
|
+
"\").then(e=>new Response(e.body.pipeThrough(new DecompressionStream(",
|
|
78
|
+
")),{headers:{\"Content-Type\":\"text/javascript\"}}).blob()).then(e=>(import(e=URL.createObjectURL(e)),URL.revokeObjectURL(e)))"
|
|
79
|
+
],
|
|
80
|
+
"css": "document.head.appendChild(document.createElement(\"style\")).innerHTML=",
|
|
81
|
+
"icon": "document.querySelector(\"link[rel=icon]\").href=",
|
|
82
|
+
"importmeta": ["{let e=import.meta,t=e.resolve,r=e.url=new URL(", ",location).href;e.resolve=function(e){return/^\\.{0,2}\\//.test(e)?new URL(e,r).href:t.apply(this,arguments)}}"]
|
|
90
83
|
};
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/getTemplate.ts
|
|
91
86
|
const template = {
|
|
92
87
|
async base(script, format, useBase128, compressor) {
|
|
93
88
|
script = await compress(format, script, useBase128, compressor);
|
|
94
|
-
|
|
95
|
-
return
|
|
89
|
+
const t = useBase128 ? files.base128 : files.base64;
|
|
90
|
+
return t[0].concat(script, t[1], JSON.stringify(format), t[2]);
|
|
96
91
|
},
|
|
97
92
|
assets(assetsJSON) {
|
|
98
|
-
|
|
93
|
+
const t = files.assets;
|
|
94
|
+
return t[0].concat(JSON.stringify(assetsJSON), t[1]);
|
|
99
95
|
},
|
|
100
96
|
css(cssSource) {
|
|
101
|
-
return files.css
|
|
97
|
+
return files.css + JSON.stringify(cssSource);
|
|
102
98
|
},
|
|
103
99
|
icon(dataURL) {
|
|
104
|
-
return files.icon
|
|
100
|
+
return files.icon + JSON.stringify(dataURL);
|
|
105
101
|
},
|
|
106
102
|
importmeta(p) {
|
|
107
|
-
|
|
103
|
+
const t = files.importmeta;
|
|
104
|
+
return t[0].concat(JSON.stringify(p), t[1]);
|
|
108
105
|
}
|
|
109
106
|
};
|
|
110
107
|
//#endregion
|
|
111
|
-
//#region
|
|
108
|
+
//#region src/dataurl.ts
|
|
112
109
|
function bufferToDataURL(name, b) {
|
|
113
110
|
return /\.svg$/i.test(name) ? svgToTinyDataUri(b.toString()) : `data:${mime.getType(name)};base64,${b.toString("base64")}`;
|
|
114
111
|
}
|
|
115
112
|
//#endregion
|
|
116
|
-
//#region
|
|
113
|
+
//#region src/kB.ts
|
|
117
114
|
function kB(size) {
|
|
118
115
|
const s = String(size).padStart(4, "0");
|
|
119
116
|
return `${s.slice(0, -3)}.${s.slice(-3)} kB`;
|
|
120
117
|
}
|
|
121
118
|
//#endregion
|
|
122
|
-
//#region
|
|
119
|
+
//#region src/options.ts
|
|
123
120
|
const defaultHtmlMinifierTerserOptions = {
|
|
124
121
|
removeAttributeQuotes: true,
|
|
125
122
|
removeComments: true,
|
|
@@ -145,12 +142,12 @@ function getInnerOptions(opt) {
|
|
|
145
142
|
};
|
|
146
143
|
}
|
|
147
144
|
//#endregion
|
|
148
|
-
//#region
|
|
145
|
+
//#region src/cutPrefix.ts
|
|
149
146
|
function cutPrefix(str, prefix) {
|
|
150
147
|
return str.startsWith(prefix) ? str.slice(prefix.length) : str;
|
|
151
148
|
}
|
|
152
149
|
//#endregion
|
|
153
|
-
//#region
|
|
150
|
+
//#region src/index.ts
|
|
154
151
|
function singleFileCompression(opt) {
|
|
155
152
|
let conf;
|
|
156
153
|
const innerOptions = getInnerOptions(opt);
|
|
@@ -195,7 +192,7 @@ async function generateBundle(bundle, config, options) {
|
|
|
195
192
|
for (const htmlFileName of bundleHTMLNames) {
|
|
196
193
|
console.log("\n " + pc.underline(pc.cyan(distURL) + pc.greenBright(bundle[htmlFileName].fileName)));
|
|
197
194
|
const htmlChunk = bundle[htmlFileName], oldHTML = htmlChunk.source, dom = new JSDOM(oldHTML), document = dom.window.document, thisDel = /* @__PURE__ */ new Set(), newJSCode = [], scriptElement = document.querySelector(`script[type=module]${assetsSrcSelector}`), scriptName = scriptElement ? cutPrefix(scriptElement.src, config.base) : "";
|
|
198
|
-
let oldSize = oldHTML
|
|
195
|
+
let oldSize = Buffer.byteLength(oldHTML);
|
|
199
196
|
if (scriptElement) {
|
|
200
197
|
scriptElement.remove();
|
|
201
198
|
scriptElement.removeAttribute("src");
|
|
@@ -210,7 +207,7 @@ async function generateBundle(bundle, config, options) {
|
|
|
210
207
|
thisDel.add(name);
|
|
211
208
|
const cssSource = bundle[name].source;
|
|
212
209
|
if (cssSource) {
|
|
213
|
-
oldSize += cssSource
|
|
210
|
+
oldSize += Buffer.byteLength(cssSource);
|
|
214
211
|
for (const name of bundleAssetsNames) if (cssSource.includes(name.slice(assetsDir.length))) globalDoNotDelete.add(name);
|
|
215
212
|
allCSS += cssSource.replace(/\s*(\/\*[^*]*\*\/)?\s*$/, "");
|
|
216
213
|
}
|
|
@@ -232,10 +229,15 @@ async function generateBundle(bundle, config, options) {
|
|
|
232
229
|
const a = bundle[bundleName];
|
|
233
230
|
if (!a) continue;
|
|
234
231
|
thisDel.add(bundleName);
|
|
235
|
-
oldSize += a.source.length;
|
|
236
232
|
let dataURL;
|
|
237
|
-
if (Object.prototype.hasOwnProperty.call(globalAssetsDataURL, name))
|
|
238
|
-
|
|
233
|
+
if (Object.prototype.hasOwnProperty.call(globalAssetsDataURL, name)) {
|
|
234
|
+
oldSize += Buffer.byteLength(a.source);
|
|
235
|
+
dataURL = globalAssetsDataURL[name];
|
|
236
|
+
} else {
|
|
237
|
+
const buf = Buffer.from(a.source);
|
|
238
|
+
oldSize += buf.length;
|
|
239
|
+
globalAssetsDataURL[name] = dataURL = bufferToDataURL(name, buf);
|
|
240
|
+
}
|
|
239
241
|
if (options.enableCompress) assetsDataURL[name] = dataURL;
|
|
240
242
|
else element.src = dataURL;
|
|
241
243
|
}
|
|
@@ -290,7 +292,7 @@ async function generateBundle(bundle, config, options) {
|
|
|
290
292
|
if (scriptElement) {
|
|
291
293
|
thisDel.add(scriptName);
|
|
292
294
|
let { code } = bundle[scriptName];
|
|
293
|
-
oldSize += code
|
|
295
|
+
oldSize += Buffer.byteLength(code);
|
|
294
296
|
code = code.replace(/;?\n?$/, "");
|
|
295
297
|
for (const name of bundleAssetsNames) {
|
|
296
298
|
const assetName = name.slice(assetsDir.length);
|
|
@@ -301,14 +303,15 @@ async function generateBundle(bundle, config, options) {
|
|
|
301
303
|
}
|
|
302
304
|
inlineHtmlAssets();
|
|
303
305
|
if (options.useImportMetaPolyfill) newJSCode.push(template.importmeta(scriptName));
|
|
304
|
-
if (/\b__VITE_PRELOAD__\b/.test(code))
|
|
306
|
+
if (/\b__VITE_PRELOAD__\b/.test(code)) if (code.startsWith("var ")) code = "var __VITE_PRELOAD__," + code.slice(4);
|
|
307
|
+
else newJSCode.push("var __VITE_PRELOAD__");
|
|
305
308
|
newJSCode.push(code);
|
|
306
309
|
} else inlineHtmlAssets();
|
|
307
310
|
let outputScript = newJSCode.join(";");
|
|
308
311
|
if (options.enableCompress) outputScript = await template.base(outputScript, options.compressFormat, options.useBase128, options.compressor);
|
|
309
312
|
else outputScript = outputScript.replaceAll("<\/script", "<\\/script");
|
|
310
|
-
htmlChunk.source = htmlChunk.source.
|
|
311
|
-
console.log(" " + pc.gray(kB(oldSize) + " -> ") + pc.cyanBright(kB(htmlChunk.source
|
|
313
|
+
htmlChunk.source = htmlChunk.source.replace(fakeScript, () => outputScript);
|
|
314
|
+
console.log(" " + pc.gray(kB(oldSize) + " -> ") + pc.cyanBright(kB(Buffer.byteLength(htmlChunk.source))) + "\n");
|
|
312
315
|
for (const name of thisDel) globalDelete.add(name);
|
|
313
316
|
}
|
|
314
317
|
if (options.removeInlinedAssetFiles) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-plugin-singlefile-compression",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.3",
|
|
4
4
|
"author": "bddjr",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Compress all assets and embeds them into dist/index.html, making it convenient to share as a single HTML file.",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
],
|
|
12
12
|
"type": "module",
|
|
13
13
|
"scripts": {
|
|
14
|
-
"build": "
|
|
14
|
+
"build": "tsc && node build.js && tsc _dist/templateRaw.ts --ignoreConfig --noEmit && cd test && node --run build",
|
|
15
15
|
"prepublishOnly": "node --run build"
|
|
16
16
|
},
|
|
17
17
|
"repository": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"compression",
|
|
33
33
|
"compress",
|
|
34
34
|
"compressionstream",
|
|
35
|
-
"
|
|
35
|
+
"decompress",
|
|
36
36
|
"decompression",
|
|
37
37
|
"decompressionstream",
|
|
38
38
|
"compression streams api",
|
|
@@ -90,13 +90,11 @@
|
|
|
90
90
|
"vite": ">=3.0.0"
|
|
91
91
|
},
|
|
92
92
|
"devDependencies": {
|
|
93
|
-
"@bddjr/rimraf": "^0.1.0",
|
|
94
93
|
"@types/jsdom": ">=28.0.0",
|
|
95
94
|
"@types/node": "^22.19.15",
|
|
96
|
-
"esbuild": "^0.27.4",
|
|
97
95
|
"rolldown": ">=1.0.0-rc.9",
|
|
98
96
|
"rolldown-plugin-dts": "^0.22.5",
|
|
99
|
-
"
|
|
97
|
+
"terser": "^5.46.1",
|
|
100
98
|
"typescript": ">=5.9.3",
|
|
101
99
|
"vite": ">=8.0.0"
|
|
102
100
|
}
|