@divriots/jampack 0.5.0 → 0.5.1

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/dist/compress.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import * as fs from 'fs/promises';
2
2
  import * as path from 'path';
3
- import { optimize as svgo } from "svgo";
3
+ import { optimize as svgo } from 'svgo';
4
4
  import { minify as htmlminifier } from 'html-minifier-terser';
5
- import { minify as csso } from "csso";
5
+ import { minify as csso } from 'csso';
6
6
  import { formatBytes } from './utils.js';
7
7
  import sharp from 'sharp';
8
8
  import swc from '@swc/core';
@@ -10,7 +10,7 @@ import $state from './state.js';
10
10
  import { globby } from 'globby';
11
11
  import config from './config.js';
12
12
  import ora from 'ora';
13
- import { getFromCacheCompressImage, addToCacheCompressImage, computeCacheHash } from './cache.js';
13
+ import { getFromCacheCompressImage, addToCacheCompressImage, computeCacheHash, } from './cache.js';
14
14
  async function compressJS(code) {
15
15
  const newjsresult = await swc.minify(code, { compress: true, mangle: true });
16
16
  return newjsresult.code;
@@ -31,6 +31,7 @@ const processFile = async (file, stats) => {
31
31
  case '.jpeg':
32
32
  case '.svg':
33
33
  case '.webp':
34
+ case '.avif':
34
35
  const newImage = await compressImageFile(file);
35
36
  if ((newImage === null || newImage === void 0 ? void 0 : newImage.data) && newImage.data.length < stats.size) {
36
37
  writeData = newImage.data;
@@ -39,7 +40,12 @@ const processFile = async (file, stats) => {
39
40
  case '.html':
40
41
  case '.htm':
41
42
  const htmldata = await fs.readFile(file);
42
- const newhtmlData = await htmlminifier(htmldata.toString(), { minifyCSS: true, minifyJS: compressInlineJS, sortClassName: true, sortAttributes: true });
43
+ const newhtmlData = await htmlminifier(htmldata.toString(), {
44
+ minifyCSS: true,
45
+ minifyJS: compressInlineJS,
46
+ sortClassName: true,
47
+ sortAttributes: true,
48
+ });
43
49
  writeData = newhtmlData;
44
50
  break;
45
51
  case '.css':
@@ -66,7 +72,7 @@ const processFile = async (file, stats) => {
66
72
  const result = {
67
73
  action: path.extname(file),
68
74
  originalSize: stats.size,
69
- compressedSize: stats.size
75
+ compressedSize: stats.size,
70
76
  };
71
77
  // Writedata
72
78
  if (writeData && writeData.length < result.originalSize) {
@@ -82,7 +88,7 @@ function createWebpOptions(opt) {
82
88
  return {
83
89
  nearLossless: opt.mode === 'lossless',
84
90
  quality: opt.quality,
85
- effort: opt.effort
91
+ effort: opt.effort,
86
92
  };
87
93
  }
88
94
  export const compressImage = async (data, options) => {
@@ -108,14 +114,15 @@ export const compressImage = async (data, options) => {
108
114
  case 'svg':
109
115
  const newData = svgo(data, {
110
116
  multipass: true,
111
- plugins: [{
112
- name: "preset-default",
117
+ plugins: [
118
+ {
119
+ name: 'preset-default',
113
120
  params: {
114
121
  overrides: {
115
122
  removeViewBox: false,
116
123
  },
117
124
  },
118
- }
125
+ },
119
126
  ],
120
127
  });
121
128
  if (newData.error || newData.modernError) {
@@ -144,10 +151,17 @@ export const compressImage = async (data, options) => {
144
151
  doSharp = true;
145
152
  toFormat = 'unchanged'; // Don't need to convert to webP again
146
153
  break;
154
+ case 'avif':
155
+ outputFormat = 'avif';
156
+ // TODO but avif is already pretty optimized
157
+ toFormat = 'unchanged';
158
+ break;
147
159
  }
148
160
  if (doSharp) {
149
161
  if (toFormat === 'webp') {
150
- sharpFile = sharpFile.webp(createWebpOptions(meta.format === 'png' ? config.image.webp.options_lossless : config.image.webp.options_lossly));
162
+ sharpFile = sharpFile.webp(createWebpOptions(meta.format === 'png'
163
+ ? config.image.webp.options_lossless
164
+ : config.image.webp.options_lossly));
151
165
  outputFormat = 'webp';
152
166
  }
153
167
  else if (toFormat === 'pjpg') {
@@ -155,13 +169,20 @@ export const compressImage = async (data, options) => {
155
169
  outputFormat = 'jpg';
156
170
  }
157
171
  if (((_a = options.resize) === null || _a === void 0 ? void 0 : _a.width) && ((_b = options.resize) === null || _b === void 0 ? void 0 : _b.height)) {
158
- sharpFile = sharpFile.resize({ ...options.resize, fit: 'fill', withoutEnlargement: true });
172
+ sharpFile = sharpFile.resize({
173
+ ...options.resize,
174
+ fit: 'fill',
175
+ withoutEnlargement: true,
176
+ });
159
177
  }
160
178
  //
161
179
  // Output image processed
162
180
  //
163
181
  // Add to cache
164
- const outputImage = { format: outputFormat, data: await sharpFile.toBuffer() };
182
+ const outputImage = {
183
+ format: outputFormat,
184
+ data: await sharpFile.toBuffer(),
185
+ };
165
186
  await addToCacheCompressImage(cacheHash, outputImage);
166
187
  // Go
167
188
  return outputImage;
package/dist/index.js CHANGED
@@ -21,11 +21,9 @@ const logo = ` __ __
21
21
  `;
22
22
  console.log(logo);
23
23
  const program = new Command();
24
+ program.name('jampack').description('Static website Optimizer').version(VERSION);
24
25
  program
25
- .name('jampack')
26
- .description('Static website Optimizer')
27
- .version(VERSION);
28
- program.command('pack', { isDefault: true })
26
+ .command('pack', { isDefault: true })
29
27
  .description('todo')
30
28
  .argument('<dir>', 'Directory to pack')
31
29
  .option('--exclude <exclude>', 'Glob to exclude')
@@ -69,26 +67,51 @@ program.parse();
69
67
  function printSummary() {
70
68
  if ($state.summary.nbFiles > 0) {
71
69
  printTitle('Summary');
72
- const dataTable = [['Action', 'Compressed', 'Original', 'Compressed', 'Gain']];
70
+ const dataTable = [
71
+ ['Action', 'Compressed', 'Original', 'Compressed', 'Gain'],
72
+ ];
73
73
  const config = {
74
74
  columns: [
75
75
  { alignment: 'left' },
76
76
  { alignment: 'right' },
77
77
  { alignment: 'right' },
78
78
  { alignment: 'right' },
79
- { alignment: 'right' }
79
+ { alignment: 'right' },
80
80
  ],
81
81
  drawHorizontalLine: (lineIndex, rowCount) => {
82
- return lineIndex === 0 || lineIndex === 1 || lineIndex === rowCount - 1 || lineIndex === rowCount;
83
- }
82
+ return (lineIndex === 0 ||
83
+ lineIndex === 1 ||
84
+ lineIndex === rowCount - 1 ||
85
+ lineIndex === rowCount);
86
+ },
84
87
  };
88
+ const unCompressedDataRows = [];
85
89
  Object.entries($state.summaryByExtension).forEach(([ext, summary]) => {
86
- if (summary.dataLenCompressed < summary.dataLenUncompressed) {
87
- const row = [ext, `${summary.nbFilesCompressed} / ${summary.nbFiles}`, formatBytes(summary.dataLenUncompressed), formatBytes(summary.dataLenCompressed), '-' + formatBytes(summary.dataLenUncompressed - summary.dataLenCompressed)];
90
+ const gain = summary.dataLenUncompressed - summary.dataLenCompressed;
91
+ const row = [
92
+ ext,
93
+ `${summary.nbFilesCompressed} / ${summary.nbFiles}`,
94
+ formatBytes(summary.dataLenUncompressed),
95
+ formatBytes(summary.dataLenCompressed),
96
+ gain > 0 ? '-' + formatBytes(gain) : '',
97
+ ];
98
+ if (gain > 0) {
88
99
  dataTable.push(row);
89
100
  }
101
+ else {
102
+ unCompressedDataRows.push(row);
103
+ }
90
104
  });
91
- const total = ['Total', `${$state.summary.nbFilesCompressed} / ${$state.summary.nbFiles}`, formatBytes($state.summary.dataLenUncompressed), formatBytes($state.summary.dataLenCompressed), '-' + formatBytes($state.summary.dataLenUncompressed - $state.summary.dataLenCompressed)];
105
+ // Add uncompress rows at the end
106
+ dataTable.push(...unCompressedDataRows);
107
+ const total = [
108
+ 'Total',
109
+ `${$state.summary.nbFilesCompressed} / ${$state.summary.nbFiles}`,
110
+ formatBytes($state.summary.dataLenUncompressed),
111
+ formatBytes($state.summary.dataLenCompressed),
112
+ '-' +
113
+ formatBytes($state.summary.dataLenUncompressed - $state.summary.dataLenCompressed),
114
+ ];
92
115
  dataTable.push(total);
93
116
  console.log(table(dataTable, config));
94
117
  }
@@ -103,7 +126,7 @@ function printIssues() {
103
126
  console.log('');
104
127
  for (let [file, list] of $state.issues) {
105
128
  console.log('▶ ' + file + '\n');
106
- list.forEach(issue => {
129
+ list.forEach((issue) => {
107
130
  issueCount++;
108
131
  console.log(`${kleur.bgYellow(` ${issue.type} `)} ${issue.msg}\n`);
109
132
  });
package/dist/optimize.js CHANGED
@@ -1,33 +1,33 @@
1
- import { globby } from "globby";
2
- import * as path from "path";
3
- import * as fs from "fs/promises";
4
- import cheerio from "cheerio";
5
- import { isNumeric } from "./utils.js";
6
- import config from "./config.js";
7
- import { compressImage } from "./compress.js";
8
- import svgToMiniDataURI from "mini-svg-data-uri";
9
- import $state from "./state.js";
10
- import kleur from "kleur";
11
- import ora from "ora";
12
- import { isLocal, Resource, translateSrc } from "./utils/resource.js";
1
+ import { globby } from 'globby';
2
+ import * as path from 'path';
3
+ import * as fs from 'fs/promises';
4
+ import cheerio from 'cheerio';
5
+ import { isNumeric } from './utils.js';
6
+ import config from './config.js';
7
+ import { compressImage } from './compress.js';
8
+ import svgToMiniDataURI from 'mini-svg-data-uri';
9
+ import $state from './state.js';
10
+ import kleur from 'kleur';
11
+ import ora from 'ora';
12
+ import { isLocal, Resource, translateSrc } from './utils/resource.js';
13
13
  async function analyse(file) {
14
- console.log("▶ " + file);
14
+ console.log('▶ ' + file);
15
15
  const html = (await fs.readFile(path.join($state.dir, file))).toString();
16
16
  const $ = cheerio.load(html, {
17
17
  withStartIndices: true,
18
18
  decodeEntities: false,
19
19
  });
20
20
  const theFold = getTheFold($);
21
- const imgs = $("img");
21
+ const imgs = $('img');
22
22
  const imgsArray = [];
23
23
  imgs.each(async (index, imgElement) => {
24
24
  imgsArray.push(imgElement);
25
25
  });
26
26
  // Process images sequentially
27
- const spinnerImg = ora({ prefixText: " " }).start();
27
+ const spinnerImg = ora({ prefixText: ' ' }).start();
28
28
  for (let i = 0; i < imgsArray.length; i++) {
29
29
  const imgElement = imgsArray[i];
30
- spinnerImg.text = kleur.dim(`<img> [${i + 1}/${imgsArray.length}] ${$(imgElement).attr("src")} `);
30
+ spinnerImg.text = kleur.dim(`<img> [${i + 1}/${imgsArray.length}] ${$(imgElement).attr('src')} `);
31
31
  const isAboveTheFold = imgElement.startIndex < theFold;
32
32
  await processImage(file, $, imgElement, isAboveTheFold);
33
33
  }
@@ -37,21 +37,21 @@ async function analyse(file) {
37
37
  const issues = $state.issues.get(file);
38
38
  if (issues) {
39
39
  spinnerImg.fail();
40
- console.log(kleur.red(` ${issues.length} issue${issues.length > 1 ? "s" : ""}`));
40
+ console.log(kleur.red(` ${issues.length} issue${issues.length > 1 ? 's' : ''}`));
41
41
  }
42
42
  else {
43
43
  spinnerImg.succeed();
44
44
  }
45
45
  // Remove the fold
46
46
  if (theFold) {
47
- $("the-fold").remove();
47
+ $('the-fold').remove();
48
48
  }
49
49
  if (!$state.args.nowrite) {
50
50
  await fs.writeFile(path.join($state.dir, file), $.html());
51
51
  }
52
52
  }
53
53
  function getTheFold($) {
54
- const theFolds = $("the-fold");
54
+ const theFolds = $('the-fold');
55
55
  if (!theFolds[0]) {
56
56
  return 0;
57
57
  }
@@ -64,10 +64,10 @@ async function processImage(htmlfile, $, imgElement, isAboveTheFold) {
64
64
  /*
65
65
  * Attribute 'src'
66
66
  */
67
- const attrib_src = img.attr("src");
67
+ const attrib_src = img.attr('src');
68
68
  if (!attrib_src) {
69
69
  $state.reportIssue(htmlfile, {
70
- type: "warn",
70
+ type: 'warn',
71
71
  msg: `Missing [src] on img - processing skipped.`,
72
72
  });
73
73
  return;
@@ -75,15 +75,15 @@ async function processImage(htmlfile, $, imgElement, isAboveTheFold) {
75
75
  /*
76
76
  * Attribute 'alt'
77
77
  */
78
- const attrib_alt = img.attr("alt");
78
+ const attrib_alt = img.attr('alt');
79
79
  if (attrib_alt === undefined) {
80
80
  $state.reportIssue(htmlfile, {
81
- type: "a11y",
81
+ type: 'a11y',
82
82
  msg: `Missing [alt] on img src="${attrib_src}" - Adding alt="" meanwhile.`,
83
83
  });
84
- img.attr("alt", "");
84
+ img.attr('alt', '');
85
85
  }
86
- if (attrib_src.startsWith("data:")) {
86
+ if (attrib_src.startsWith('data:')) {
87
87
  // Data URI image
88
88
  // TODO: try to compress it
89
89
  return;
@@ -91,26 +91,26 @@ async function processImage(htmlfile, $, imgElement, isAboveTheFold) {
91
91
  /*
92
92
  * Attribute 'loading'
93
93
  */
94
- const attr_loading = img.attr("loading");
94
+ const attr_loading = img.attr('loading');
95
95
  if (isAboveTheFold) {
96
- img.removeAttr("loading");
97
- img.attr("fetchpriority", "high");
96
+ img.removeAttr('loading');
97
+ img.attr('fetchpriority', 'high');
98
98
  }
99
99
  else {
100
100
  switch (attr_loading) {
101
101
  case undefined:
102
102
  // Go lazy by default
103
- img.attr("loading", "lazy");
103
+ img.attr('loading', 'lazy');
104
104
  break;
105
- case "eager":
106
- img.removeAttr("loading");
105
+ case 'eager':
106
+ img.removeAttr('loading');
107
107
  break;
108
- case "lazy":
108
+ case 'lazy':
109
109
  // Don't touch it
110
110
  break;
111
111
  default:
112
112
  $state.reportIssue(htmlfile, {
113
- type: "invalid",
113
+ type: 'invalid',
114
114
  msg: `Invalid [loading]="${attr_loading}" on img src="${attrib_src}"`,
115
115
  });
116
116
  }
@@ -118,7 +118,7 @@ async function processImage(htmlfile, $, imgElement, isAboveTheFold) {
118
118
  /*
119
119
  * Attribute 'decoding'
120
120
  */
121
- img.attr("decoding", "async");
121
+ img.attr('decoding', 'async');
122
122
  if (!isLocal(attrib_src)) {
123
123
  // Image not local, don't touch it
124
124
  return;
@@ -130,7 +130,7 @@ async function processImage(htmlfile, $, imgElement, isAboveTheFold) {
130
130
  // No file -> give up
131
131
  if (!originalImage) {
132
132
  $state.reportIssue(htmlfile, {
133
- type: "erro",
133
+ type: 'erro',
134
134
  msg: `Can't find img on disk src="${attrib_src}"`,
135
135
  });
136
136
  return;
@@ -145,7 +145,7 @@ async function processImage(htmlfile, $, imgElement, isAboveTheFold) {
145
145
  // Let's avoid to optimize same images twice
146
146
  $state.optimizedFiles.add(originalImage.filePathAbsolute);
147
147
  newImage = await compressImage(await originalImage.getData(), {
148
- toFormat: canBeProgressiveJpeg ? "pjpg" : "webp",
148
+ toFormat: canBeProgressiveJpeg ? 'pjpg' : 'webp',
149
149
  });
150
150
  if ((newImage === null || newImage === void 0 ? void 0 : newImage.data) &&
151
151
  (newImage.data.length < (await originalImage.getLen()) ||
@@ -154,12 +154,13 @@ async function processImage(htmlfile, $, imgElement, isAboveTheFold) {
154
154
  // Do we need to add an new extension?
155
155
  const newExtension = `.${newImage.format}`;
156
156
  const additionalExtension = path.extname(originalImage.filePathAbsolute) === newExtension
157
- ? ""
157
+ ? ''
158
158
  : newExtension;
159
159
  const newFilename = originalImage.filePathAbsolute + additionalExtension;
160
160
  if (!$state.args.nowrite) {
161
161
  fs.writeFile(newFilename, newImage.data);
162
162
  }
163
+ // Mark new file as optimized
163
164
  $state.compressedFiles.add(newFilename);
164
165
  // Report compression result
165
166
  $state.reportSummary({
@@ -169,7 +170,15 @@ async function processImage(htmlfile, $, imgElement, isAboveTheFold) {
169
170
  originalSize: await originalImage.getLen(),
170
171
  compressedSize: newImage.data.length,
171
172
  });
172
- img.attr("src", attrib_src + additionalExtension);
173
+ img.attr('src', attrib_src + additionalExtension);
174
+ }
175
+ else {
176
+ // Report non-compression
177
+ $state.reportSummary({
178
+ action: path.extname(originalImage.filePathAbsolute),
179
+ originalSize: await originalImage.getLen(),
180
+ compressedSize: await originalImage.getLen(),
181
+ });
173
182
  }
174
183
  }
175
184
  /*
@@ -183,22 +192,22 @@ async function processImage(htmlfile, $, imgElement, isAboveTheFold) {
183
192
  if (newImage && newImage.data.length <= config.image.embed_size) {
184
193
  let datauri = undefined;
185
194
  switch (newImage.format) {
186
- case "svg":
195
+ case 'svg':
187
196
  datauri = svgToMiniDataURI(newImage.data.toString());
188
197
  break;
189
- case "webp":
190
- datauri = `data:image/webp;base64,${newImage.data.toString("base64")}`;
198
+ case 'webp':
199
+ datauri = `data:image/webp;base64,${newImage.data.toString('base64')}`;
191
200
  break;
192
- case "jpg":
193
- case "png":
201
+ case 'jpg':
202
+ case 'png':
194
203
  // TODO but not possible in current code
195
204
  break;
196
205
  }
197
206
  if (datauri) {
198
207
  isEmbed = true;
199
- img.attr("src", datauri);
200
- img.removeAttr("loading");
201
- img.removeAttr("decoding");
208
+ img.attr('src', datauri);
209
+ img.removeAttr('loading');
210
+ img.removeAttr('decoding');
202
211
  $state.reportSummary({
203
212
  action: `${newImage.format}->embed`,
204
213
  originalSize: await originalImage.getLen(),
@@ -217,13 +226,13 @@ async function processImage(htmlfile, $, imgElement, isAboveTheFold) {
217
226
  //
218
227
  // Stop here if svg
219
228
  //
220
- if ((await originalImage.getExt()) === "svg") {
229
+ if ((await originalImage.getExt()) === 'svg') {
221
230
  return;
222
231
  }
223
232
  /*
224
233
  * Attribute 'srcset'
225
234
  */
226
- const attr_srcset = img.attr("srcset");
235
+ const attr_srcset = img.attr('srcset');
227
236
  if (attr_srcset) {
228
237
  // If srcset is set, don't touch it.
229
238
  // The compress pass will compress the images
@@ -233,9 +242,9 @@ async function processImage(htmlfile, $, imgElement, isAboveTheFold) {
233
242
  // Generate image set
234
243
  const ext = path.extname(attrib_src);
235
244
  const fullbasename = attrib_src.slice(0, -ext.length);
236
- const imageSrc = (addition) => `${fullbasename}${addition}.${canBeProgressiveJpeg ? "jpg" : "webp"}`;
245
+ const imageSrc = (addition) => `${fullbasename}${addition}.${canBeProgressiveJpeg ? 'jpg' : 'webp'}`;
237
246
  // Start from original image
238
- let new_srcset = "";
247
+ let new_srcset = '';
239
248
  // Start reduction
240
249
  const step = 300; //px
241
250
  const ratio = w / h;
@@ -250,7 +259,7 @@ async function processImage(htmlfile, $, imgElement, isAboveTheFold) {
250
259
  $state.compressedFiles.add(absoluteFilename);
251
260
  const compressedImage = await compressImage(await originalImage.getData(), {
252
261
  resize: { width: valueW, height: valueH },
253
- toFormat: canBeProgressiveJpeg ? "pjpg" : "webp",
262
+ toFormat: canBeProgressiveJpeg ? 'pjpg' : 'webp',
254
263
  });
255
264
  if ((compressedImage === null || compressedImage === void 0 ? void 0 : compressedImage.data) && !$state.args.nowrite) {
256
265
  fs.writeFile(absoluteFilename, compressedImage.data);
@@ -262,28 +271,28 @@ async function processImage(htmlfile, $, imgElement, isAboveTheFold) {
262
271
  valueH = Math.trunc(valueW / ratio);
263
272
  }
264
273
  if (new_srcset) {
265
- img.attr("srcset", `${img.attr("src")} ${w}w` + new_srcset);
274
+ img.attr('srcset', `${img.attr('src')} ${w}w` + new_srcset);
266
275
  }
267
276
  }
268
277
  }
269
278
  catch (e) {
270
279
  $state.reportIssue(htmlfile, {
271
- type: "erro",
280
+ type: 'erro',
272
281
  msg: e.message ||
273
282
  `Unexpected error while processing image: ${JSON.stringify(e)}`,
274
283
  });
275
284
  }
276
285
  }
277
286
  async function setImageSize(htmlfile, img, image) {
278
- let width = img.attr("width");
279
- let height = img.attr("height");
287
+ let width = img.attr('width');
288
+ let height = img.attr('height');
280
289
  let width_new = undefined;
281
290
  let height_new = undefined;
282
291
  // Check valid values
283
292
  if (width !== undefined) {
284
293
  if (!isNumeric(width)) {
285
294
  $state.reportIssue(htmlfile, {
286
- type: "warn",
295
+ type: 'warn',
287
296
  msg: `Invalid width attribute "${width}" format - overriding`,
288
297
  });
289
298
  width = undefined;
@@ -292,7 +301,7 @@ async function setImageSize(htmlfile, img, image) {
292
301
  if (height !== undefined) {
293
302
  if (!isNumeric(height)) {
294
303
  $state.reportIssue(htmlfile, {
295
- type: "warn",
304
+ type: 'warn',
296
305
  msg: `Invalid height attribute "${height}" format - overriding`,
297
306
  });
298
307
  height = undefined;
@@ -316,7 +325,7 @@ async function setImageSize(htmlfile, img, image) {
316
325
  const imageRatio = Math.round(originalRatio * 10) / 10;
317
326
  if (providedRatio !== imageRatio) {
318
327
  $state.reportIssue(htmlfile, {
319
- type: "warn",
328
+ type: 'warn',
320
329
  msg: `Image aspect ratio in HTML (${providedRatio}) differs from image aspect ratio (${imageRatio}) - fix width and height or let jampack fill them.`,
321
330
  });
322
331
  }
@@ -334,20 +343,20 @@ async function setImageSize(htmlfile, img, image) {
334
343
  }
335
344
  else {
336
345
  // No width or height provided - set both to image size
337
- if ((await image.getExt()) === "svg") {
346
+ if ((await image.getExt()) === 'svg') {
338
347
  // svg with no height and width has special sizing by browsers
339
348
  // They size it inside 300x150 unless they have width and height
340
349
  // attributes
341
350
  // Load svg
342
351
  const c = cheerio.load(await image.getData(), {});
343
- const svg = c("svg").first();
344
- const svg_viewbox = svg.attr("viewbox"); // bug in cheerio here, should be "viewBox"
345
- const svg_width = svg.attr("width");
346
- const svg_height = svg.attr("height");
352
+ const svg = c('svg').first();
353
+ const svg_viewbox = svg.attr('viewbox'); // bug in cheerio here, should be "viewBox"
354
+ const svg_width = svg.attr('width');
355
+ const svg_height = svg.attr('height');
347
356
  // Calculate aspect ratio from viewbox
348
357
  let svg_aspectratio_from_viewbox = undefined;
349
358
  if (svg_viewbox) {
350
- const box = svg_viewbox.split(" ");
359
+ const box = svg_viewbox.split(' ');
351
360
  const w = parseInt(box[2], 10);
352
361
  const h = parseInt(box[3], 10);
353
362
  svg_aspectratio_from_viewbox = w / h;
@@ -395,17 +404,17 @@ async function setImageSize(htmlfile, img, image) {
395
404
  if (width_new !== undefined && height_new !== undefined) {
396
405
  const result_w = Math.round(width_new);
397
406
  const result_h = Math.round(height_new);
398
- img.attr("width", result_w.toFixed(0));
399
- img.attr("height", result_h.toFixed(0));
407
+ img.attr('width', result_w.toFixed(0));
408
+ img.attr('height', result_h.toFixed(0));
400
409
  return [result_w, result_h];
401
410
  }
402
411
  // Something when wrong
403
412
  throw new Error(`Unexpected issue when resolving image size "${image.src}"`);
404
413
  }
405
414
  export async function optimize(exclude) {
406
- const glob = ["**/*.{htm,html}"];
415
+ const glob = ['**/*.{htm,html}'];
407
416
  if (exclude)
408
- glob.push("!" + exclude);
417
+ glob.push('!' + exclude);
409
418
  const paths = await globby(glob, { cwd: $state.dir });
410
419
  // Sequential async
411
420
  for (const file of paths) {
package/dist/state.js CHANGED
@@ -9,7 +9,7 @@ export class GlobalState {
9
9
  nbFiles: 0,
10
10
  nbFilesCompressed: 0,
11
11
  dataLenCompressed: 0,
12
- dataLenUncompressed: 0
12
+ dataLenUncompressed: 0,
13
13
  };
14
14
  this.summaryByExtension = {};
15
15
  }
@@ -27,6 +27,7 @@ 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);
30
31
  if (r.action) {
31
32
  let summary = this.summaryByExtension[r.action];
32
33
  if (!summary) {
@@ -34,7 +35,7 @@ export class GlobalState {
34
35
  nbFiles: 0,
35
36
  nbFilesCompressed: 0,
36
37
  dataLenUncompressed: 0,
37
- dataLenCompressed: 0
38
+ dataLenCompressed: 0,
38
39
  };
39
40
  this.summaryByExtension[r.action] = summary;
40
41
  }
@@ -44,7 +45,6 @@ export class GlobalState {
44
45
  summary.dataLenCompressed += r.compressedSize;
45
46
  }
46
47
  }
47
- ;
48
48
  }
49
49
  const state = new GlobalState();
50
50
  export default state;
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
- export type ImageFormat = 'webp' | 'svg' | 'jpg' | 'png' | undefined;
2
+ export type ImageFormat = 'webp' | 'svg' | 'jpg' | 'png' | 'avif' | undefined;
3
3
  export type Image = {
4
4
  format: ImageFormat;
5
5
  data: Buffer;
@@ -27,13 +27,14 @@ export class Resource {
27
27
  case 'png':
28
28
  case 'webp':
29
29
  case 'tif':
30
+ case 'avif':
30
31
  let sharpFile = await sharp(await this.getData(), { animated: true });
31
32
  const meta = await sharpFile.metadata();
32
33
  this.image_meta = {
33
34
  width: meta.width,
34
35
  height: meta.height,
35
36
  isProgressive: meta.isProgressive || false,
36
- hasAlpha: meta.hasAlpha || false
37
+ hasAlpha: meta.hasAlpha || false,
37
38
  };
38
39
  break;
39
40
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@divriots/jampack",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Packer for static websites",
5
5
  "author": "Georges Gomes @georges-gomes",
6
6
  "bin": {
@@ -49,7 +49,7 @@
49
49
  },
50
50
  "scripts": {
51
51
  "watch": "pnpm build --watch",
52
- "try": "shx rm -fr demo && cp -R demo_ref demo && node ./dist/index.js",
52
+ "try": "shx rm -fr demo && cp -R demo_avif demo && node ./dist/index.js ./demo",
53
53
  "build": "shx rm -rf dist && tsc -b",
54
54
  "lint": "eslint . --ext .ts --config .eslintrc"
55
55
  },