@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 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`?
@@ -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
- const newcssData = await csso(cssdata.toString()).css;
54
- if (newcssData) {
55
- writeData = newcssData;
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
- switch (meta.format) {
114
- case 'svg':
115
- const newData = svgo(data, {
116
- multipass: true,
117
- plugins: [
118
- {
119
- name: 'preset-default',
120
- params: {
121
- overrides: {
122
- removeViewBox: false,
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
- if (newData.error || newData.modernError) {
129
- console.log(`Error processing svg ${data}`);
130
- return undefined;
131
- }
132
- return { format: 'svg', data: Buffer.from(newData.data, 'utf8') };
133
- case 'png':
134
- outputFormat = 'png';
135
- if (toFormat === 'unchanged') {
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
- doSharp = true;
139
- break;
140
- case 'jpeg':
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
- doSharp = true;
147
- break;
148
- case 'webp':
149
- outputFormat = 'webp';
150
- sharpFile = sharpFile.webp(createWebpOptions(config.image.webp.options_lossly) || {});
151
- doSharp = true;
152
- toFormat = 'unchanged'; // Don't need to convert to webP again
153
- break;
154
- case 'avif':
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
- if (((_a = options.resize) === null || _a === void 0 ? void 0 : _a.width) && ((_b = options.resize) === null || _b === void 0 ? void 0 : _b.height)) {
172
- sharpFile = sharpFile.resize({
173
- ...options.resize,
174
- fit: 'fill',
175
- withoutEnlargement: true,
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
- // Unknown input format or error while processing
191
- return undefined;
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' : 'webp'}`;
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' : 'webp',
268
+ toFormat: canBeProgressiveJpeg ? 'pjpg' : targetFormat,
263
269
  });
264
- if ((compressedImage === null || compressedImage === void 0 ? void 0 : compressedImage.data) && !$state.args.nowrite) {
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.5.1",
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
  },