vite-plugin-singlefile-compression 2.4.0 → 2.4.2

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.
Files changed (3) hide show
  1. package/README.md +5 -5
  2. package/dist/index.js +82 -71
  3. package/package.json +6 -6
package/README.md CHANGED
@@ -170,21 +170,21 @@ type: `boolean`
170
170
  Preview: https://bddjr.github.io/vite-plugin-singlefile-compression/#/
171
171
 
172
172
  ```
173
- vite v8.0.8 building client environment for production...
173
+ vite v8.0.10 building client environment for production...
174
174
  ✓ 43 modules transformed.
175
175
  rendering chunks (1)...
176
176
 
177
- vite-plugin-singlefile-compression 2.4.0 deflate-raw base128-ascii
177
+ vite-plugin-singlefile-compression 2.4.1 deflate-raw base128-ascii
178
178
 
179
179
  file:///D:/code/js/vite-plugin-singlefile-compression/test/dist/index.html
180
- 126.497 kB -> 59.589 kB
180
+ 126.509 kB -> 59.596 kB
181
181
 
182
182
  Finish.
183
183
 
184
184
  computing gzip size...
185
- dist/index.html 59.58 kB │ gzip: 44.90 kB
185
+ dist/index.html 59.59 kB │ gzip: 44.92 kB
186
186
 
187
- ✓ built in 293ms
187
+ ✓ built in 307ms
188
188
  ```
189
189
 
190
190
  ## Clone
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import zlib from "zlib";
9
9
  import svgToTinyDataUri from "mini-svg-data-uri";
10
10
  import { lookup } from "mrmime";
11
11
  //#region package.json
12
- var version = "2.4.0";
12
+ var version = "2.4.2";
13
13
  //#endregion
14
14
  //#region src/compress.ts
15
15
  const compressors = {
@@ -77,8 +77,6 @@ const files = {
77
77
  "\").then(e=>new Response(e.body.pipeThrough(new DecompressionStream(",
78
78
  "))).text()).then(e=>{var t=document.createElement(\"script\");t.type=\"module\",t.innerHTML=e,document.head.appendChild(t)})"
79
79
  ],
80
- "css": "document.head.appendChild(document.createElement(\"style\")).innerHTML=",
81
- "icon": "document.querySelector(\"link[rel=icon]\").href=",
82
80
  "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)}}"]
83
81
  };
84
82
  //#endregion
@@ -93,12 +91,6 @@ const template = {
93
91
  const t = files.assets;
94
92
  return t[0].concat(JSON.stringify(assetsJSON), t[1]);
95
93
  },
96
- css(cssSource) {
97
- return files.css + JSON.stringify(cssSource);
98
- },
99
- icon(dataURL) {
100
- return files.icon + JSON.stringify(dataURL);
101
- },
102
94
  importmeta(p) {
103
95
  const t = files.importmeta;
104
96
  return t[0].concat(JSON.stringify(p), t[1]);
@@ -188,12 +180,12 @@ async function generateBundle(bundle, config, options) {
188
180
  bundle[options.rename].fileName = options.rename;
189
181
  delete bundle["index.html"];
190
182
  }
191
- const distURL = pathToFileURL(config.build.outDir).href + "/", assetsDir = path.posix.join(config.build.assetsDir, "/"), assetsDirWithBase = config.base + assetsDir, assetsHrefSelector = `[href^="${assetsDirWithBase}"]`, assetsSrcSelector = `[src^="${assetsDirWithBase}"]`, fakeScript = `_vitePluginSinglefileCompression(${Date.now()})`, globalDelete = /* @__PURE__ */ new Set(), globalDoNotDelete = /* @__PURE__ */ new Set(), globalRemoveDistFileNames = /* @__PURE__ */ new Set(), globalAssetsDataURL = {}, globalPublicFilesCache = {}, bundleAssetsNames = [], bundleHTMLNames = [];
183
+ const distURL = pathToFileURL(config.build.outDir).href + "/", assetsDir = path.posix.join(config.build.assetsDir, "/"), assetsDirWithBase = config.base + assetsDir, assetsHrefSelector = `[href^="${assetsDirWithBase}"]`, assetsSrcSelector = `[src^="${assetsDirWithBase}"]`, fakeScript = `_VITE${Math.random().toString(36).slice(2, 10)}()`, globalDelete = /* @__PURE__ */ new Set(), globalDoNotDelete = /* @__PURE__ */ new Set(), globalRemoveDistFileNames = /* @__PURE__ */ new Set(), globalAssetsDataURL = {}, globalPublicFilesCache = {}, bundleAssetsNames = [], bundleHTMLNames = [];
192
184
  for (const name in bundle) if (name.startsWith(assetsDir)) bundleAssetsNames.push(name);
193
185
  else if (/\.html$/i.test(name)) bundleHTMLNames.push(name);
194
186
  for (const htmlFileName of bundleHTMLNames) {
195
187
  console.log("\n " + pc.underline(pc.cyan(distURL) + pc.greenBright(bundle[htmlFileName].fileName)));
196
- 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) : "";
188
+ 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) : "", compressHeadElements = [];
197
189
  let oldSize = Buffer.byteLength(oldHTML);
198
190
  if (scriptElement) {
199
191
  scriptElement.remove();
@@ -211,16 +203,18 @@ async function generateBundle(bundle, config, options) {
211
203
  if (cssSource) {
212
204
  oldSize += Buffer.byteLength(cssSource);
213
205
  for (const name of bundleAssetsNames) if (cssSource.includes(name.slice(assetsDir.length))) globalDoNotDelete.add(name);
214
- allCSS += cssSource.replace(/\s*(\/\*[^*]*\*\/)?\s*$/, "");
206
+ allCSS += cssSource.replace(/(\s*\/\*([^*]|\*(?!\/))*\*\/)*\s*$/, "");
215
207
  }
216
208
  if (options.enableCompress) element.remove();
217
209
  }
218
- if (allCSS) if (options.enableCompress) newJSCode.push(template.css(allCSS));
219
- else {
210
+ if (allCSS) {
220
211
  const e = document.createElement("style");
221
212
  e.innerHTML = allCSS;
222
- linkStylesheet[0].before(e);
223
- for (const e of linkStylesheet) e.remove();
213
+ if (options.enableCompress) compressHeadElements.push(e);
214
+ else {
215
+ linkStylesheet[0].before(e);
216
+ for (const e of linkStylesheet) e.remove();
217
+ }
224
218
  }
225
219
  const assetsDataURL = {};
226
220
  if (options.tryInlineHtmlAssets) for (const element of document.querySelectorAll(assetsSrcSelector)) {
@@ -245,61 +239,73 @@ async function generateBundle(bundle, config, options) {
245
239
  }
246
240
  if (options.enableCompress) element.src = `data:${name}`;
247
241
  }
248
- let linkFavicon = document.querySelector(`link[rel=icon][href^="${config.base}"]`);
249
- {
250
- const link_shortcut_icon = document.querySelector(`link[rel="shortcut icon"][href^="${config.base}"]`);
251
- if (link_shortcut_icon) if (linkFavicon) link_shortcut_icon.remove();
242
+ const createIconElement = (href) => {
243
+ const e = document.createElement("link");
244
+ e.rel = "icon";
245
+ if (href != null) e.href = href;
246
+ return e;
247
+ };
248
+ const getPublicIcon = (faviconName) => {
249
+ if (Object.prototype.hasOwnProperty.call(globalPublicFilesCache, faviconName)) return globalPublicFilesCache[faviconName];
250
+ let _path = path.join(config.build.outDir, faviconName);
251
+ if (fs.existsSync(_path)) globalRemoveDistFileNames.add(faviconName);
252
252
  else {
253
- link_shortcut_icon.rel = "icon";
254
- linkFavicon = link_shortcut_icon;
253
+ _path = path.join(config.publicDir, faviconName);
254
+ if (!fs.existsSync(_path)) return null;
255
255
  }
256
- }
257
- let faviconName = "favicon.ico";
258
- let faviconIsDataURL = false;
259
- if (linkFavicon) {
260
- faviconName = linkFavicon.href;
261
- faviconIsDataURL = /^data:/i.test(faviconName);
256
+ const b = fs.readFileSync(_path);
257
+ return globalPublicFilesCache[faviconName] = {
258
+ buffer: b,
259
+ dataURL: bufferToDataURL(faviconName, b),
260
+ size: b.length
261
+ };
262
+ };
263
+ const linkFaviconAll = document.querySelectorAll(`link[rel=icon][href]:not([href=""]),link[rel="shortcut icon"][href]:not([href=""])`);
264
+ if (linkFaviconAll.length == 0) {
265
+ if (options.tryInlineHtmlPublicIcon) {
266
+ const fileCache = getPublicIcon("favicon.ico");
267
+ if (fileCache) {
268
+ oldSize += fileCache.size;
269
+ const e = createIconElement(fileCache.dataURL);
270
+ if (options.enableCompressInlinedIcon) compressHeadElements.push(e);
271
+ else document.head.appendChild(e);
272
+ }
273
+ }
274
+ } else for (const linkFavicon of linkFaviconAll) {
275
+ let faviconName = linkFavicon.href;
276
+ const faviconIsDataURL = /^data:/i.test(faviconName);
262
277
  if (!faviconIsDataURL) faviconName = cutPrefix(faviconName, config.base);
263
- }
264
- function setFaviconDataURL(dataURL) {
265
- if (options.enableCompressInlinedIcon) {
266
- newJSCode.push(template.icon(dataURL));
267
- if (linkFavicon) linkFavicon.href = "data:";
268
- else document.head.insertAdjacentHTML("beforeend", "<link rel=\"icon\" href=\"data:\">");
269
- } else if (linkFavicon) linkFavicon.href = dataURL;
270
- else {
271
- const e = document.head.appendChild(document.createElement("link"));
272
- e.rel = "icon";
273
- e.href = dataURL;
278
+ const setFaviconDataURL = (dataURL) => {
279
+ if (options.enableCompressInlinedIcon) if (linkFavicon) {
280
+ linkFavicon.remove();
281
+ linkFavicon.href = dataURL;
282
+ compressHeadElements.push(linkFavicon);
283
+ } else compressHeadElements.push(createIconElement(dataURL));
284
+ else if (linkFavicon) linkFavicon.href = dataURL;
285
+ else document.head.appendChild(createIconElement(dataURL));
286
+ };
287
+ if (faviconIsDataURL) {
288
+ if (options.enableCompressInlinedIcon) {
289
+ linkFavicon.remove();
290
+ compressHeadElements.push(linkFavicon);
291
+ }
292
+ } else if (bundleAssetsNames.includes(faviconName)) {
293
+ const asset = bundle[faviconName];
294
+ if (asset) {
295
+ setFaviconDataURL(bufferToDataURL(faviconName, Buffer.from(asset.source)));
296
+ thisDel.add(faviconName);
297
+ }
298
+ } else if (options.tryInlineHtmlPublicIcon) {
299
+ const fileCache = getPublicIcon(faviconName);
300
+ if (fileCache) {
301
+ oldSize += fileCache.size;
302
+ setFaviconDataURL(fileCache.dataURL);
303
+ }
274
304
  }
275
305
  }
276
- if (faviconIsDataURL) {
277
- if (options.enableCompressInlinedIcon) {
278
- newJSCode.push(template.icon(faviconName));
279
- linkFavicon.href = "data:";
280
- }
281
- } else if (bundleAssetsNames.includes(faviconName)) {
282
- const asset = bundle[faviconName];
283
- if (asset) {
284
- setFaviconDataURL(bufferToDataURL(faviconName, Buffer.from(asset.source)));
285
- thisDel.add(faviconName);
286
- }
287
- } else if (options.tryInlineHtmlPublicIcon) try {
288
- if (!Object.prototype.hasOwnProperty.call(globalPublicFilesCache, faviconName)) {
289
- let Path = path.join(config.build.outDir, faviconName);
290
- if (fs.existsSync(Path)) globalRemoveDistFileNames.add(faviconName);
291
- else Path = path.join(config.publicDir, faviconName);
292
- const b = fs.readFileSync(Path);
293
- globalPublicFilesCache[faviconName] = {
294
- dataURL: bufferToDataURL(faviconName, b),
295
- size: b.length
296
- };
297
- }
298
- const { dataURL, size } = globalPublicFilesCache[faviconName];
299
- oldSize += size;
300
- setFaviconDataURL(dataURL);
301
- } catch (e) {
302
- if (linkFavicon) console.error(e);
306
+ if (compressHeadElements.length) {
307
+ const html = compressHeadElements.map((v) => v.outerHTML).join("");
308
+ newJSCode.push(`document.head.insertAdjacentHTML("beforeend",${JSON.stringify(html)})`);
303
309
  }
304
310
  htmlChunk.source = dom.serialize();
305
311
  if (options.htmlMinifierTerser) htmlChunk.source = await minify(htmlChunk.source, options.htmlMinifierTerser);
@@ -313,7 +319,7 @@ async function generateBundle(bundle, config, options) {
313
319
  thisDel.add(scriptName);
314
320
  let { code } = bundle[scriptName];
315
321
  oldSize += Buffer.byteLength(code);
316
- code = code.replace(/;?\n?$/, "");
322
+ code = code.replace(/;?\s*$/, "");
317
323
  for (const name of bundleAssetsNames) {
318
324
  const assetName = name.slice(assetsDir.length);
319
325
  if (code.includes(assetName)) globalDoNotDelete.add(name);
@@ -333,10 +339,15 @@ async function generateBundle(bundle, config, options) {
333
339
  if (options.removeInlinedAssetFiles) {
334
340
  for (const name of globalDelete) if (!globalDoNotDelete.has(name)) delete bundle[name];
335
341
  }
336
- if (options.removeInlinedPublicIconFiles) for (const name of globalRemoveDistFileNames) try {
337
- fs.rmSync(path.join(config.build.outDir, name), { force: true });
338
- } catch (e) {
339
- console.error(e);
342
+ if (options.removeInlinedPublicIconFiles) {
343
+ const { outDir } = config.build;
344
+ const mustStartsWith = path.resolve(outDir) + path.sep;
345
+ for (const name of globalRemoveDistFileNames) try {
346
+ const _path = path.resolve(outDir, name);
347
+ if (_path.startsWith(mustStartsWith)) fs.rmSync(_path, { force: true });
348
+ } catch (e) {
349
+ console.error(e);
350
+ }
340
351
  }
341
352
  console.log(pc.green("Finish.") + pc.reset("\n"));
342
353
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-singlefile-compression",
3
- "version": "2.4.0",
3
+ "version": "2.4.2",
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.",
@@ -92,7 +92,7 @@
92
92
  "@types/html-minifier-terser": ">=7.0.2",
93
93
  "base128-ascii": ">=5.0.0",
94
94
  "html-minifier-terser": ">=7.2.0",
95
- "jsdom": ">=29.0.2",
95
+ "jsdom": "^29.1.0",
96
96
  "mini-svg-data-uri": ">=1.4.4",
97
97
  "mrmime": ">=2.0.1",
98
98
  "picocolors": ">=1.1.1"
@@ -101,10 +101,10 @@
101
101
  "@bddjr/types-rollupoptions-4.43.0": "4.43.0",
102
102
  "@types/jsdom": ">=28.0.1",
103
103
  "@types/node": "^24.12.2",
104
- "rolldown": ">=1.0.0-rc.15",
104
+ "rolldown": ">=1.0.0-rc.17",
105
105
  "rolldown-plugin-dts": ">=0.23.2",
106
- "terser": ">=5.46.1",
107
- "typescript": ">=6.0.2",
108
- "vite": ">=8.0.8"
106
+ "terser": ">=5.46.2",
107
+ "typescript": ">=6.0.3",
108
+ "vite": ">=8.0.10"
109
109
  }
110
110
  }