@divriots/jampack 0.5.1 → 0.6.0
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 +2 -0
- package/dist/compress.d.ts +1 -1
- package/dist/compress.js +107 -76
- package/dist/optimize.js +17 -3
- package/dist/state.js +0 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -68,6 +68,8 @@ See [the list of features](https://jampack.divriots.com).
|
|
|
68
68
|
- [WebComponents.dev](https://WebComponents.dev)
|
|
69
69
|
- [LWC.studio](https://lwc.studio)
|
|
70
70
|
- [Components.studio](https://components.studio)
|
|
71
|
+
- [keycloak.ch](https://keycloak.ch)
|
|
72
|
+
|
|
71
73
|
> Add yours here
|
|
72
74
|
|
|
73
75
|
## Why is it called `jampack`?
|
package/dist/compress.d.ts
CHANGED
|
@@ -2,6 +2,6 @@ import sharp from 'sharp';
|
|
|
2
2
|
import type { Image } from './types.js';
|
|
3
3
|
export declare const compressImage: (data: Buffer, options: {
|
|
4
4
|
resize?: sharp.ResizeOptions;
|
|
5
|
-
toFormat?: 'webp' | 'pjpg' | 'unchanged';
|
|
5
|
+
toFormat?: 'webp' | 'pjpg' | 'avif' | 'unchanged';
|
|
6
6
|
}) => Promise<Image | undefined>;
|
|
7
7
|
export declare function compress(exclude: string): Promise<void>;
|
package/dist/compress.js
CHANGED
|
@@ -3,6 +3,7 @@ import * as path from 'path';
|
|
|
3
3
|
import { optimize as svgo } from 'svgo';
|
|
4
4
|
import { minify as htmlminifier } from 'html-minifier-terser';
|
|
5
5
|
import { minify as csso } from 'csso';
|
|
6
|
+
import { transform as lightcss } from 'lightningcss';
|
|
6
7
|
import { formatBytes } from './utils.js';
|
|
7
8
|
import sharp from 'sharp';
|
|
8
9
|
import swc from '@swc/core';
|
|
@@ -50,9 +51,34 @@ const processFile = async (file, stats) => {
|
|
|
50
51
|
break;
|
|
51
52
|
case '.css':
|
|
52
53
|
const cssdata = await fs.readFile(file);
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
// Compress with csso
|
|
55
|
+
const cssoCSSData = Buffer.from(await csso(cssdata.toString(), { comments: false }).css);
|
|
56
|
+
// Compress with lightningcss
|
|
57
|
+
let lightCSSData;
|
|
58
|
+
try {
|
|
59
|
+
lightCSSData = lightcss({
|
|
60
|
+
filename: 'style.css',
|
|
61
|
+
code: cssdata,
|
|
62
|
+
minify: true,
|
|
63
|
+
sourceMap: false,
|
|
64
|
+
}).code;
|
|
65
|
+
}
|
|
66
|
+
catch (e) {
|
|
67
|
+
// Error while processing with lightningcss
|
|
68
|
+
// Ignore
|
|
69
|
+
}
|
|
70
|
+
// Pick the best
|
|
71
|
+
if (cssoCSSData && lightCSSData) {
|
|
72
|
+
writeData =
|
|
73
|
+
cssoCSSData.length <= lightCSSData.length
|
|
74
|
+
? cssoCSSData
|
|
75
|
+
: lightCSSData;
|
|
76
|
+
}
|
|
77
|
+
else if (cssoCSSData && !lightCSSData) {
|
|
78
|
+
writeData = cssoCSSData;
|
|
79
|
+
}
|
|
80
|
+
else if (!cssoCSSData && lightCSSData) {
|
|
81
|
+
writeData = lightCSSData;
|
|
56
82
|
}
|
|
57
83
|
break;
|
|
58
84
|
case '.js':
|
|
@@ -108,87 +134,92 @@ export const compressImage = async (data, options) => {
|
|
|
108
134
|
// Skip animated images for the moment.
|
|
109
135
|
return undefined;
|
|
110
136
|
}
|
|
111
|
-
let doSharp = false;
|
|
112
137
|
let outputFormat;
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
},
|
|
138
|
+
// Special case for svg
|
|
139
|
+
if (meta.format === 'svg') {
|
|
140
|
+
const newData = svgo(data, {
|
|
141
|
+
multipass: true,
|
|
142
|
+
plugins: [
|
|
143
|
+
{
|
|
144
|
+
name: 'preset-default',
|
|
145
|
+
params: {
|
|
146
|
+
overrides: {
|
|
147
|
+
removeViewBox: false,
|
|
124
148
|
},
|
|
125
149
|
},
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
});
|
|
153
|
+
if (newData.error || newData.modernError) {
|
|
154
|
+
console.log(`Error processing svg ${data}`);
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
return { format: 'svg', data: Buffer.from(newData.data, 'utf8') };
|
|
158
|
+
}
|
|
159
|
+
// The bitmap images
|
|
160
|
+
if (toFormat === 'unchanged') {
|
|
161
|
+
switch (meta.format) {
|
|
162
|
+
case 'png':
|
|
136
163
|
sharpFile = sharpFile.png(config.image.png.options || {});
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
case 'jpg':
|
|
142
|
-
outputFormat = 'jpg';
|
|
143
|
-
if (toFormat === 'unchanged') {
|
|
164
|
+
outputFormat = 'png';
|
|
165
|
+
break;
|
|
166
|
+
case 'jpeg':
|
|
167
|
+
case 'jpg':
|
|
144
168
|
sharpFile = sharpFile.jpeg(config.image.jpeg.options || {});
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
outputFormat = 'avif';
|
|
156
|
-
// TODO but avif is already pretty optimized
|
|
157
|
-
toFormat = 'unchanged';
|
|
158
|
-
break;
|
|
159
|
-
}
|
|
160
|
-
if (doSharp) {
|
|
161
|
-
if (toFormat === 'webp') {
|
|
162
|
-
sharpFile = sharpFile.webp(createWebpOptions(meta.format === 'png'
|
|
163
|
-
? config.image.webp.options_lossless
|
|
164
|
-
: config.image.webp.options_lossly));
|
|
165
|
-
outputFormat = 'webp';
|
|
166
|
-
}
|
|
167
|
-
else if (toFormat === 'pjpg') {
|
|
168
|
-
sharpFile = sharpFile.jpeg({ ...config.image.jpeg.options, progressive: true } || {});
|
|
169
|
-
outputFormat = 'jpg';
|
|
169
|
+
outputFormat = 'jpg';
|
|
170
|
+
break;
|
|
171
|
+
case 'webp':
|
|
172
|
+
sharpFile = sharpFile.webp(createWebpOptions(config.image.webp.options_lossly) || {});
|
|
173
|
+
outputFormat = 'webp';
|
|
174
|
+
break;
|
|
175
|
+
case 'avif':
|
|
176
|
+
sharpFile = sharpFile.avif();
|
|
177
|
+
outputFormat = 'avif';
|
|
178
|
+
break;
|
|
170
179
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
// To format
|
|
183
|
+
switch (toFormat) {
|
|
184
|
+
case 'pjpg':
|
|
185
|
+
sharpFile = sharpFile.jpeg({ ...config.image.jpeg.options, progressive: true } || {});
|
|
186
|
+
outputFormat = 'jpg';
|
|
187
|
+
break;
|
|
188
|
+
case 'webp':
|
|
189
|
+
sharpFile = sharpFile.webp(createWebpOptions(meta.format === 'png'
|
|
190
|
+
? config.image.webp.options_lossless
|
|
191
|
+
: config.image.webp.options_lossly));
|
|
192
|
+
outputFormat = 'webp';
|
|
193
|
+
break;
|
|
194
|
+
case 'avif':
|
|
195
|
+
sharpFile = sharpFile.avif({ effort: 6 });
|
|
196
|
+
outputFormat = 'avif';
|
|
197
|
+
break;
|
|
177
198
|
}
|
|
178
|
-
//
|
|
179
|
-
// Output image processed
|
|
180
|
-
//
|
|
181
|
-
// Add to cache
|
|
182
|
-
const outputImage = {
|
|
183
|
-
format: outputFormat,
|
|
184
|
-
data: await sharpFile.toBuffer(),
|
|
185
|
-
};
|
|
186
|
-
await addToCacheCompressImage(cacheHash, outputImage);
|
|
187
|
-
// Go
|
|
188
|
-
return outputImage;
|
|
189
199
|
}
|
|
190
|
-
//
|
|
191
|
-
|
|
200
|
+
// Unknow input format or output format
|
|
201
|
+
// Can't do
|
|
202
|
+
if (!outputFormat)
|
|
203
|
+
return undefined;
|
|
204
|
+
// If resize is requested
|
|
205
|
+
if (((_a = options.resize) === null || _a === void 0 ? void 0 : _a.width) && ((_b = options.resize) === null || _b === void 0 ? void 0 : _b.height)) {
|
|
206
|
+
sharpFile = sharpFile.resize({
|
|
207
|
+
...options.resize,
|
|
208
|
+
fit: 'fill',
|
|
209
|
+
withoutEnlargement: true,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
//
|
|
213
|
+
// Output image processed
|
|
214
|
+
//
|
|
215
|
+
// Add to cache
|
|
216
|
+
const outputImage = {
|
|
217
|
+
format: outputFormat,
|
|
218
|
+
data: await sharpFile.toBuffer(),
|
|
219
|
+
};
|
|
220
|
+
await addToCacheCompressImage(cacheHash, outputImage);
|
|
221
|
+
// Go
|
|
222
|
+
return outputImage;
|
|
192
223
|
};
|
|
193
224
|
const compressImageFile = async (file) => {
|
|
194
225
|
const buffer = await fs.readFile(file);
|
package/dist/optimize.js
CHANGED
|
@@ -240,9 +240,12 @@ async function processImage(htmlfile, $, imgElement, isAboveTheFold) {
|
|
|
240
240
|
}
|
|
241
241
|
else {
|
|
242
242
|
// Generate image set
|
|
243
|
+
// Avif => Avif
|
|
244
|
+
// png, jpg, ... => WebP
|
|
245
|
+
const targetFormat = (await originalImage.getMime()) === 'image/avif' ? 'avif' : 'webp';
|
|
243
246
|
const ext = path.extname(attrib_src);
|
|
244
247
|
const fullbasename = attrib_src.slice(0, -ext.length);
|
|
245
|
-
const imageSrc = (addition) => `${fullbasename}${addition}.${canBeProgressiveJpeg ? 'jpg' :
|
|
248
|
+
const imageSrc = (addition) => `${fullbasename}${addition}.${canBeProgressiveJpeg ? 'jpg' : targetFormat}`;
|
|
246
249
|
// Start from original image
|
|
247
250
|
let new_srcset = '';
|
|
248
251
|
// Start reduction
|
|
@@ -250,6 +253,9 @@ async function processImage(htmlfile, $, imgElement, isAboveTheFold) {
|
|
|
250
253
|
const ratio = w / h;
|
|
251
254
|
let valueW = w - step;
|
|
252
255
|
let valueH = Math.trunc(valueW / ratio);
|
|
256
|
+
let previousImageSize = (newImage === null || newImage === void 0 ? void 0 : newImage.data)
|
|
257
|
+
? Math.min(newImage.data.length, await originalImage.getLen())
|
|
258
|
+
: await originalImage.getLen();
|
|
253
259
|
while (valueW > config.image.srcset_min_width) {
|
|
254
260
|
const src = imageSrc(`@${valueW}w`);
|
|
255
261
|
const absoluteFilename = translateSrc($state.dir, path.dirname(htmlfile), src);
|
|
@@ -259,11 +265,19 @@ async function processImage(htmlfile, $, imgElement, isAboveTheFold) {
|
|
|
259
265
|
$state.compressedFiles.add(absoluteFilename);
|
|
260
266
|
const compressedImage = await compressImage(await originalImage.getData(), {
|
|
261
267
|
resize: { width: valueW, height: valueH },
|
|
262
|
-
toFormat: canBeProgressiveJpeg ? 'pjpg' :
|
|
268
|
+
toFormat: canBeProgressiveJpeg ? 'pjpg' : targetFormat,
|
|
263
269
|
});
|
|
264
|
-
if ((compressedImage === null || compressedImage === void 0 ? void 0 : compressedImage.data)
|
|
270
|
+
if (!(compressedImage === null || compressedImage === void 0 ? void 0 : compressedImage.data) ||
|
|
271
|
+
compressedImage.data.length >= previousImageSize) {
|
|
272
|
+
// jampack was not able to compress below the original image
|
|
273
|
+
// Let's move on
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
if (!$state.args.nowrite) {
|
|
265
277
|
fs.writeFile(absoluteFilename, compressedImage.data);
|
|
266
278
|
}
|
|
279
|
+
// Add previous smaller file size
|
|
280
|
+
previousImageSize = compressedImage.data.length;
|
|
267
281
|
}
|
|
268
282
|
new_srcset += `, ${src} ${valueW}w`;
|
|
269
283
|
// reduce size
|
package/dist/state.js
CHANGED
|
@@ -27,7 +27,6 @@ export class GlobalState {
|
|
|
27
27
|
state.summary.nbFilesCompressed += isCompressed;
|
|
28
28
|
state.summary.dataLenUncompressed += r.originalSize;
|
|
29
29
|
state.summary.dataLenCompressed += r.compressedSize;
|
|
30
|
-
console.log(r.action);
|
|
31
30
|
if (r.action) {
|
|
32
31
|
let summary = this.summaryByExtension[r.action];
|
|
33
32
|
if (!summary) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@divriots/jampack",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Packer for static websites",
|
|
5
5
|
"author": "Georges Gomes @georges-gomes",
|
|
6
6
|
"bin": {
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"hasha": "^5.2.2",
|
|
35
35
|
"html-minifier-terser": "^7.1.0",
|
|
36
36
|
"kleur": "^4.1.5",
|
|
37
|
+
"lightningcss": "^1.18.0",
|
|
37
38
|
"mini-svg-data-uri": "^1.4.4",
|
|
38
39
|
"ora": "^6.1.2",
|
|
39
40
|
"sharp": "^0.31.2",
|
|
@@ -49,7 +50,7 @@
|
|
|
49
50
|
},
|
|
50
51
|
"scripts": {
|
|
51
52
|
"watch": "pnpm build --watch",
|
|
52
|
-
"try": "shx rm -fr demo && cp -R demo_avif demo && node ./dist/index.js ./demo",
|
|
53
|
+
"try": "shx rm -fr demo && cp -R demo_avif demo && node ./dist/index.js ./demo --nocache",
|
|
53
54
|
"build": "shx rm -rf dist && tsc -b",
|
|
54
55
|
"lint": "eslint . --ext .ts --config .eslintrc"
|
|
55
56
|
},
|