Skip to content

Commit fe1bad4

Browse files
committed
add Options.enableCompress
1 parent 45dc9ba commit fe1bad4

4 files changed

Lines changed: 149 additions & 84 deletions

File tree

README.md

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,36 @@ More info see [src/options.ts](src/options.ts)
4949

5050
```ts
5151
export interface Options {
52+
/**
53+
* Enable compress.
54+
* @default true
55+
*/
56+
enableCompress?: boolean
57+
58+
/**
59+
* Use Base128 to encode compressed script.
60+
* If false, use Base64.
61+
* https://www.npmjs.com/package/base128-ascii
62+
* @default true
63+
*/
64+
useBase128?: boolean
65+
66+
/**
67+
* Compress format.
68+
*
69+
* https://developer.mozilla.org/en-US/docs/Web/API/DecompressionStream/DecompressionStream
70+
*
71+
* @type {"deflate-raw" | "deflate" | "gzip" | "brotli" | "zstd" | "deflateRaw" | "gz" | "br" | "brotliCompress" | "zstandard" | "zst"}
72+
*
73+
* @default "deflate-raw"
74+
*/
75+
compressFormat?: CompressFormat | CompressFormatAlias
76+
77+
/**
78+
* Custom compressor.
79+
*/
80+
compressor?: Compressor
81+
5282
/**
5383
* Rename index.html
5484
*/
@@ -84,30 +114,6 @@ export interface Options {
84114
*/
85115
removeInlinedPublicIconFiles?: boolean
86116

87-
/**
88-
* Use Base128 to encode gzipped script.
89-
* If false, use Base64.
90-
* https://www.npmjs.com/package/base128-ascii
91-
* @default true
92-
*/
93-
useBase128?: boolean
94-
95-
/**
96-
* Compress format.
97-
*
98-
* https://developer.mozilla.org/en-US/docs/Web/API/DecompressionStream/DecompressionStream
99-
*
100-
* @type {"deflate-raw" | "deflate" | "gzip" | "brotli" | "zstd" | "deflateRaw" | "gz" | "br" | "brotliCompress" | "zstandard" | "zst"}
101-
*
102-
* @default "deflate-raw"
103-
*/
104-
compressFormat?: CompressFormat | CompressFormatAlias
105-
106-
/**
107-
* Custom compressor.
108-
*/
109-
compressor?: Compressor
110-
111117
/**
112118
* Use import.meta polyfill.
113119
* @default true
@@ -128,13 +134,14 @@ rendering chunks (1)...
128134
vite-plugin-singlefile-compression 2.1.0 deflate-raw
129135
130136
file:///D:/code/js/vite-plugin-singlefile-compression/test/dist/index.html
131-
106.506 kB -> 47.477 kB
137+
106.564 kB -> 47.458 kB
132138
133139
Finish.
134140
135-
dist/index.html 47.47 kB
141+
computing gzip size...
142+
dist/index.html 47.45 kB │ gzip: 41.35 kB
136143
137-
✓ built in 274ms
144+
✓ built in 299ms
138145
```
139146

140147
![](effect.jpg)

src/index.ts

Lines changed: 78 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,25 @@ import { version } from './getVersion.js'
1313
import { template } from './getTemplate.js'
1414
import { bufferToDataURL } from "./dataurl.js"
1515
import { kB } from "./kB.js"
16-
import { getInnerOptions, Options, innerOptions, HtmlMinifierOptions } from "./options.js"
16+
import { getInnerOptions, Options, InnerOptions as InnerOptions, HtmlMinifierOptions, defaultHtmlMinifierTerserOptions } from "./options.js"
1717
import { cutPrefix } from "./cutPrefix.js"
1818
import { CompressFormat, CompressFormatAlias, Compressor } from "./compress.js"
1919

2020
export function singleFileCompression(opt?: Options): PluginOption {
2121
let conf: ResolvedConfig
22+
const innerOptions = getInnerOptions(opt)
2223
return {
2324
name: "singleFileCompression",
2425
enforce: "post",
25-
config: setConfig,
26-
configResolved(c) { conf = c },
27-
generateBundle: (_, bundle) => generateBundle(bundle, conf, getInnerOptions(opt)),
26+
config(...args) {
27+
return setConfig.call(this, innerOptions, ...args)
28+
},
29+
configResolved(c) {
30+
conf = c
31+
},
32+
generateBundle(outputOptions, bundle) {
33+
return generateBundle(bundle, conf, innerOptions)
34+
},
2835
}
2936
}
3037

@@ -36,18 +43,17 @@ export {
3643
CompressFormat,
3744
CompressFormatAlias,
3845
Compressor,
46+
defaultHtmlMinifierTerserOptions,
3947
}
4048

41-
function setConfig(this: ConfigPluginContext, config: UserConfig, env: ConfigEnv) {
49+
function setConfig(this: ConfigPluginContext, opt: InnerOptions, config: UserConfig, env: ConfigEnv) {
4250
config.base ??= './'
4351

4452
const build = (config.build ??= {})
4553

4654
build.cssCodeSplit ??= false
4755
build.assetsInlineLimit ??= () => true
48-
build.chunkSizeWarningLimit ??= Number.MAX_SAFE_INTEGER
4956
build.modulePreload ?? build.polyfillModulePreload ?? (build.modulePreload = { polyfill: false })
50-
build.reportCompressedSize ??= false
5157

5258
if (this.meta.rolldownVersion) {
5359
// Vite 8
@@ -66,8 +72,13 @@ function setConfig(this: ConfigPluginContext, config: UserConfig, env: ConfigEnv
6672
}
6773
}
6874

69-
async function generateBundle(bundle: OutputBundle, config: ResolvedConfig, options: innerOptions) {
70-
console.log(pc.reset('\n\n') + pc.cyan('vite-plugin-singlefile-compression ' + version) + ' ' + pc.green(options.compressFormat))
75+
async function generateBundle(bundle: OutputBundle, config: ResolvedConfig, options: InnerOptions) {
76+
console.log(
77+
pc.reset('\n\n') +
78+
pc.cyan('vite-plugin-singlefile-compression ' + version) +
79+
' ' +
80+
(options.enableCompress ? pc.green(options.compressFormat) : pc.red('disable compress'))
81+
)
7182

7283
// rename
7384
if (options.rename
@@ -113,7 +124,7 @@ async function generateBundle(bundle: OutputBundle, config: ResolvedConfig, opti
113124
for (const name in bundle) {
114125
if (name.startsWith(assetsDir))
115126
bundleAssetsNames.push(name)
116-
else if (name.endsWith('.html'))
127+
else if (/\.html$/i.test(name))
117128
bundleHTMLNames.push(name)
118129
}
119130

@@ -136,6 +147,7 @@ async function generateBundle(bundle: OutputBundle, config: ResolvedConfig, opti
136147
if (scriptElement) {
137148
scriptElement.remove()
138149
scriptElement.removeAttribute('src')
150+
scriptElement.removeAttribute('crossorigin')
139151
scriptElement.innerHTML = fakeScript
140152
document.body.appendChild(scriptElement)
141153
} else {
@@ -144,7 +156,8 @@ async function generateBundle(bundle: OutputBundle, config: ResolvedConfig, opti
144156

145157
// get css tag
146158
let allCSS = ''
147-
for (const element of document.querySelectorAll<HTMLLinkElement>(`link[rel=stylesheet]${assetsHrefSelector}`)) {
159+
const linkStylesheet = document.querySelectorAll<HTMLLinkElement>(`link[rel=stylesheet]${assetsHrefSelector}`)
160+
for (const element of linkStylesheet) {
148161
const name = cutPrefix(element.href, config.base)
149162
thisDel.add(name)
150163
const css = bundle[name] as OutputAsset
@@ -157,35 +170,54 @@ async function generateBundle(bundle: OutputBundle, config: ResolvedConfig, opti
157170
globalDoNotDelete.add(name)
158171
}
159172
// add script for load css
160-
allCSS += cssSource.replace(/\n$/, '')
173+
allCSS += cssSource.replace(/(\/\*[^*]*\*\/)?\s*$/, '')
161174
}
162175
// remove tag
163-
element.remove()
176+
if (options.enableCompress)
177+
element.remove()
178+
}
179+
if (allCSS) {
180+
if (options.enableCompress) {
181+
newJSCode.push(template.css(allCSS))
182+
} else {
183+
const e = document.createElement('style')
184+
e.innerHTML = allCSS
185+
linkStylesheet[0].before(e)
186+
for (const e of linkStylesheet) {
187+
e.remove()
188+
}
189+
}
164190
}
165-
newJSCode.push(template.css(allCSS))
166191

167192
// inline html assets
168193
const assetsDataURL = {} as { [key: string]: string }
169194
if (options.tryInlineHtmlAssets) {
170-
for (const element of (document.querySelectorAll(assetsSrcSelector) as NodeListOf<HTMLImageElement>)) {
195+
for (const element of document.querySelectorAll<HTMLImageElement>(assetsSrcSelector)) {
171196
const name = cutPrefix(element.src, assetsDirWithBase)
172-
if (name.endsWith('.js'))
197+
if (/\.js$/i.test(name))
173198
continue
174-
if (!Object.prototype.hasOwnProperty.call(assetsDataURL, name)) {
199+
if (!options.enableCompress || !Object.prototype.hasOwnProperty.call(assetsDataURL, name)) {
175200
const bundleName = assetsDir + name
176201
const a = bundle[bundleName] as OutputAsset
177202
if (!a)
178203
continue
179204
thisDel.add(bundleName)
180205
oldSize += a.source.length
181-
if (!Object.prototype.hasOwnProperty.call(globalAssetsDataURL, name))
182-
globalAssetsDataURL[name] = bufferToDataURL(name, Buffer.from(
183-
//@ts-ignore
184-
a.source
185-
))
186-
assetsDataURL[name] = globalAssetsDataURL[name]
206+
let dataURL: string
207+
if (Object.prototype.hasOwnProperty.call(globalAssetsDataURL, name)) {
208+
dataURL = globalAssetsDataURL[name]
209+
} else {
210+
globalAssetsDataURL[name] = dataURL = bufferToDataURL(name, Buffer.from(a.source))
211+
}
212+
if (options.enableCompress) {
213+
assetsDataURL[name] = dataURL
214+
} else {
215+
element.src = dataURL
216+
}
217+
}
218+
if (options.enableCompress) {
219+
element.src = `data:${name}`
187220
}
188-
element.src = `data:${name}`
189221
}
190222
}
191223

@@ -225,10 +257,20 @@ async function generateBundle(bundle: OutputBundle, config: ResolvedConfig, opti
225257
}
226258
const { dataURL, size } = globalPublicFilesCache[iconName]
227259
oldSize += size
228-
newJSCode.push(template.icon(dataURL))
229-
if (!element) {
230-
// add link icon tag
231-
document.head.insertAdjacentHTML('beforeend', '<link rel="icon" href="data:">')
260+
if (options.enableCompress) {
261+
newJSCode.push(template.icon(dataURL))
262+
if (!element) {
263+
// add link icon tag
264+
document.head.insertAdjacentHTML('beforeend', '<link rel="icon" href="data:">')
265+
}
266+
} else {
267+
if (element) {
268+
element.href = dataURL
269+
} else {
270+
const e = document.head.appendChild(document.createElement('link'))
271+
e.rel = 'icon'
272+
e.href = dataURL
273+
}
232274
}
233275
} catch (e) {
234276
if (element) console.error(e)
@@ -280,9 +322,14 @@ async function generateBundle(bundle: OutputBundle, config: ResolvedConfig, opti
280322
inlineHtmlAssets()
281323
}
282324

283-
htmlChunk.source = htmlChunk.source.split(fakeScript, 2).join(
284-
template.base(newJSCode.join(';'), options.compressFormat, options.useBase128, options.compressor)
285-
)
325+
let outputScript = newJSCode.join(';')
326+
if (options.enableCompress) {
327+
outputScript = template.base(outputScript, options.compressFormat, options.useBase128, options.compressor)
328+
} else {
329+
outputScript = outputScript.replaceAll('</script', '<\\/script')
330+
}
331+
332+
htmlChunk.source = htmlChunk.source.split(fakeScript, 2).join(outputScript)
286333

287334
// log
288335
console.log(" " + pc.gray(kB(oldSize) + " -> ") + pc.cyanBright(kB(htmlChunk.source.length)) + '\n')

src/options.ts

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,36 @@ import { Options as HtmlMinifierOptions } from 'html-minifier-terser'
22
import { CompressFormat, compressFormatAlias, CompressFormatAlias, Compressor } from './compress.js'
33

44
export interface Options {
5+
/**
6+
* Enable compress.
7+
* @default true
8+
*/
9+
enableCompress?: boolean
10+
11+
/**
12+
* Use Base128 to encode compressed script.
13+
* If false, use Base64.
14+
* https://www.npmjs.com/package/base128-ascii
15+
* @default true
16+
*/
17+
useBase128?: boolean
18+
19+
/**
20+
* Compress format.
21+
*
22+
* https://developer.mozilla.org/en-US/docs/Web/API/DecompressionStream/DecompressionStream
23+
*
24+
* @type {"deflate-raw" | "deflate" | "gzip" | "brotli" | "zstd" | "deflateRaw" | "gz" | "br" | "brotliCompress" | "zstandard" | "zst"}
25+
*
26+
* @default "deflate-raw"
27+
*/
28+
compressFormat?: CompressFormat | CompressFormatAlias
29+
30+
/**
31+
* Custom compressor.
32+
*/
33+
compressor?: Compressor
34+
535
/**
636
* Rename index.html
737
*/
@@ -37,30 +67,6 @@ export interface Options {
3767
*/
3868
removeInlinedPublicIconFiles?: boolean
3969

40-
/**
41-
* Use Base128 to encode gzipped script.
42-
* If false, use Base64.
43-
* https://www.npmjs.com/package/base128-ascii
44-
* @default true
45-
*/
46-
useBase128?: boolean
47-
48-
/**
49-
* Compress format.
50-
*
51-
* https://developer.mozilla.org/en-US/docs/Web/API/DecompressionStream/DecompressionStream
52-
*
53-
* @type {"deflate-raw" | "deflate" | "gzip" | "brotli" | "zstd" | "deflateRaw" | "gz" | "br" | "brotliCompress" | "zstandard" | "zst"}
54-
*
55-
* @default "deflate-raw"
56-
*/
57-
compressFormat?: CompressFormat | CompressFormatAlias
58-
59-
/**
60-
* Custom compressor.
61-
*/
62-
compressor?: Compressor
63-
6470
/**
6571
* Use import.meta polyfill.
6672
* @default true
@@ -77,7 +83,8 @@ export const defaultHtmlMinifierTerserOptions: HtmlMinifierOptions = {
7783
minifyJS: false,
7884
}
7985

80-
export interface innerOptions {
86+
export interface InnerOptions {
87+
enableCompress: boolean
8188
rename?: string
8289
htmlMinifierTerser: HtmlMinifierOptions | false
8390
tryInlineHtmlAssets: boolean
@@ -90,9 +97,12 @@ export interface innerOptions {
9097
useImportMetaPolyfill: boolean
9198
}
9299

93-
export function getInnerOptions(opt?: Options): innerOptions {
100+
export function getInnerOptions(opt?: Options): InnerOptions {
94101
opt ||= {}
95102
return {
103+
enableCompress:
104+
opt.enableCompress ?? true,
105+
96106
rename:
97107
opt.rename == null
98108
? undefined

test/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
</head>
1010
<body>
1111
<div id="app"></div>
12+
<!-- <img src="./src/assets/logo.svg" alt=""> -->
1213
<script type="module" src="/src/main.ts"></script>
1314
</body>
1415
</html>

0 commit comments

Comments
 (0)