@divriots/jampack 0.20.2 → 0.21.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/LICENSE CHANGED
File without changes
package/README.md CHANGED
File without changes
package/dist/cache.js CHANGED
@@ -4,7 +4,7 @@ import * as fs from 'fs/promises';
4
4
  import $state from './state.js';
5
5
  import { CACHE_VERSIONS } from './packagejson.js';
6
6
  const listOfCategories = ['img', 'img-ext'];
7
- const CACHE_FOLDER = '.jampack/cache';
7
+ const CACHE_FOLDER = $state.args.cache_folder || '.jampack/cache';
8
8
  async function cleanCache(full) {
9
9
  if (full) {
10
10
  try {
@@ -3,7 +3,7 @@ const default_options = {
3
3
  add_css_reset_as: 'inline',
4
4
  },
5
5
  css: {
6
- inline_critical_css: false, // Doesn't work yet
6
+ inline_critical_css: false,
7
7
  },
8
8
  js: {
9
9
  compressor: 'esbuild',
package/dist/index.js CHANGED
@@ -38,11 +38,14 @@ program
38
38
  .option('--fail', 'Exits with a non-zero return code if issues.')
39
39
  .option('--onlyoptim', 'Only optimize (PASS 1).')
40
40
  .option('--onlycomp', 'Only compress (PASS 2).')
41
+ .option('--cache_folder <cache_folder>', 'Default: .jampack/cache')
41
42
  .option('--cleancache', 'Clean cache before running')
42
43
  .option('--nocache', 'Run with no use of cache')
43
44
  .action(async (dir, options) => {
45
+ // Arguments
44
46
  $state.dir = dir;
45
47
  $state.args = options;
48
+ // Print options
46
49
  if (options) {
47
50
  console.log('Options:');
48
51
  console.log(options);
package/dist/optimize.js CHANGED
@@ -155,7 +155,7 @@ function getTheFold($) {
155
155
  return 0;
156
156
  }
157
157
  async function processImage(htmlfile, $, imgElement, isAboveTheFold) {
158
- var _a, _b;
158
+ var _a, _b, _c;
159
159
  const img = $(imgElement);
160
160
  /*
161
161
  * Attribute 'src'
@@ -264,19 +264,17 @@ async function processImage(htmlfile, $, imgElement, isAboveTheFold) {
264
264
  let srcToFormat = 'unchanged';
265
265
  if (isAboveTheFold) {
266
266
  // Above the fold, nothing beats progressive JPEG for LCP
267
- // but it's not transparent
268
- if ((_b = (await originalImage.getImageMeta())) === null || _b === void 0 ? void 0 : _b.hasAlpha) {
269
- // TODO some images have Alpha metadata but have 0 alpha pixels
270
- // We should be able to detect these to take advantage of Progressive JPEG
267
+ // But jpeg doesn't support transparency
268
+ if ((_b = (await originalImage.getImageMeta())) === null || _b === void 0 ? void 0 : _b.isOpaque) {
269
+ srcToFormat = 'jpeg+progressive';
270
+ }
271
+ else {
271
272
  srcToFormat =
272
273
  (await originalImage.getMime()) === 'image/avif' ||
273
274
  (await originalImage.getMime()) === 'image/webp'
274
275
  ? 'unchanged'
275
276
  : 'webp';
276
277
  }
277
- else {
278
- srcToFormat = 'jpeg+progressive';
279
- }
280
278
  }
281
279
  else if (isInPicture) {
282
280
  // srcToFormat should not change if in picture
@@ -428,18 +426,29 @@ async function processImage(htmlfile, $, imgElement, isAboveTheFold) {
428
426
  // Only try to generate better images
429
427
  // TODO generate more compatible formats (avif => webp/jpeg/png)
430
428
  //
431
- switch (await originalImage.getMime()) {
432
- case 'image/jpeg':
429
+ const mime = await originalImage.getMime();
430
+ switch (mime) {
433
431
  case 'image/png':
432
+ case 'image/jpeg':
434
433
  sourcesToGenerate.push({
435
434
  mime: 'image/webp',
436
435
  format: 'webp',
437
436
  });
438
437
  case 'image/webp':
439
- sourcesToGenerate.push({
440
- mime: 'image/avif',
441
- format: 'avif',
442
- });
438
+ // Only add AVIF source for lossy images
439
+ // AVIF don't perform well on lossless images
440
+ // PNG and WebP will perform better
441
+ // https://www.reddit.com/r/jpegxl/comments/l9ta2u/how_does_lossless_jpegxl_compared_to_png/
442
+ // https://twitter.com/jonsneyers/status/1346389917816008704?s=19
443
+ const isLossless = mime === 'image/png' ||
444
+ (mime === 'image/webp' &&
445
+ ((_c = (await originalImage.getImageMeta())) === null || _c === void 0 ? void 0 : _c.isLossless));
446
+ if (!isLossless) {
447
+ sourcesToGenerate.push({
448
+ mime: 'image/avif',
449
+ format: 'avif',
450
+ });
451
+ }
443
452
  case 'image/avif':
444
453
  // Nothing better
445
454
  break;
@@ -4,6 +4,8 @@ export function inlineCriticalCss(path, html) {
4
4
  const critters = new Critters({
5
5
  compress: false,
6
6
  fonts: false,
7
+ reduceInlineStyles: false,
8
+ inlineThreshold: 0,
7
9
  logLevel: 'info',
8
10
  path,
9
11
  });
package/dist/state.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export type Args = {
2
2
  nowrite?: boolean;
3
3
  nocache?: boolean;
4
+ cache_folder?: string;
4
5
  };
5
6
  export type ReportItem = {
6
7
  action: string;
@@ -5,7 +5,8 @@ type ImageMeta = {
5
5
  width: number | undefined;
6
6
  height: number | undefined;
7
7
  isProgressive: boolean;
8
- hasAlpha: boolean;
8
+ isOpaque: boolean;
9
+ isLossless: boolean;
9
10
  };
10
11
  /**
11
12
  * File extension, mime and data are loaded only on demand, then cached.
@@ -21,21 +21,37 @@ export class Resource {
21
21
  async getImageMeta() {
22
22
  if (this.image_meta === undefined) {
23
23
  const ext = await this.getExt();
24
+ let isLossless = false;
24
25
  switch (ext) {
25
26
  case 'svg':
26
27
  case 'gif':
27
- case 'jpg':
28
28
  case 'png':
29
- case 'webp':
30
29
  case 'tif':
30
+ isLossless = true;
31
+ case 'webp':
32
+ if (!isLossless) {
33
+ try {
34
+ // TODO check if webp is lossless
35
+ // const info = await WebPInfo.from(await this.getData());
36
+ // isLossless = info.summary.isLossless;
37
+ }
38
+ catch (e) {
39
+ console.warn('Failed to get WebP info');
40
+ }
41
+ }
31
42
  case 'avif':
43
+ // TODO
44
+ // Check for lossless avif
45
+ case 'jpg':
32
46
  let sharpFile = await sharp(await this.getData(), { animated: true });
33
47
  const meta = await sharpFile.metadata();
48
+ const stats = await sharpFile.stats();
34
49
  this.image_meta = {
35
50
  width: meta.width,
36
51
  height: meta.height,
37
52
  isProgressive: meta.isProgressive || false,
38
- hasAlpha: meta.hasAlpha || false,
53
+ isOpaque: stats.isOpaque,
54
+ isLossless,
39
55
  };
40
56
  break;
41
57
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@divriots/jampack",
3
- "version": "0.20.2",
3
+ "version": "0.21.0",
4
4
  "cache-version": {
5
5
  "img": "v1",
6
6
  "img-ext": "v1"
@@ -22,45 +22,46 @@
22
22
  "dependencies": {
23
23
  "@divriots/cheerio": "1.0.0-rc.12",
24
24
  "@proload/core": "^0.3.3",
25
- "@swc/core": "^1.3.82",
25
+ "@swc/core": "^1.3.86",
26
26
  "add": "^2.0.6",
27
27
  "commander": "^10.0.1",
28
28
  "critters": "^0.0.20",
29
29
  "deepmerge": "^4.3.1",
30
- "esbuild": "^0.19.2",
30
+ "esbuild": "^0.19.3",
31
31
  "file-type": "^18.5.0",
32
32
  "globby": "^13.2.2",
33
33
  "hasha": "^5.2.2",
34
34
  "html-minifier-terser": "^7.2.0",
35
35
  "kleur": "^4.1.5",
36
- "lightningcss": "^1.21.5",
36
+ "lightningcss": "^1.22.0",
37
37
  "mini-svg-data-uri": "^1.4.4",
38
38
  "ora": "^7.0.1",
39
39
  "quicklink": "^2.3.0",
40
- "sharp": "^0.32.5",
40
+ "sharp": "^0.32.6",
41
41
  "svgo": "^3.0.2",
42
42
  "table": "^6.8.1",
43
- "undici": "^5.23.0"
43
+ "undici": "^5.24.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/html-minifier-terser": "^7.0.0",
47
- "@types/node": "^20.5.7",
47
+ "@types/node": "^20.6.2",
48
48
  "shx": "^0.3.3",
49
49
  "ts-node": "^10.2.1",
50
50
  "tslib": "^2.6.2",
51
51
  "typescript": "^5.2.2"
52
52
  },
53
- "engines": {
54
- "node": ">=14.0.0"
55
- },
56
- "bugs": "https://github.com/divriots/jampack/issues",
57
- "keywords": [],
58
- "types": "dist/index.d.ts",
59
53
  "scripts": {
60
54
  "watch": "pnpm build --watch",
61
- "try": "shx rm -fr demo && cp -R demo_ref demo && node ./dist/index.js ./demo --nocache",
55
+ "try": "shx rm -fr demo && cp -R demo_drc demo && node ./dist/index.js ./demo --nocache",
62
56
  "build": "shx rm -rf dist && tsc -b",
63
57
  "lint": "eslint . --ext .ts --config .eslintrc",
58
+ "prepublishOnly": "pnpm build",
64
59
  "cleanAllDotJampack": "find -s . | grep '.jampack$' | xargs rm -fr"
65
- }
66
- }
60
+ },
61
+ "engines": {
62
+ "node": ">=14.0.0"
63
+ },
64
+ "bugs": "https://github.com/divriots/jampack/issues",
65
+ "keywords": [],
66
+ "types": "dist/index.d.ts"
67
+ }