@divriots/jampack 0.22.1 → 0.23.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
@@ -94,6 +94,74 @@ becomes responsive with multiple formats including AVIF:
94
94
  </picture>
95
95
  ```
96
96
 
97
+ [Read more](/features/optimize-images)
98
+
99
+ ### CDN images become responsive
100
+
101
+ ```html
102
+ <img
103
+ src="https://images.unsplash.com/photo-1513002749550-c59d786b8e6c?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
104
+ alt="Clouds in the sky by Taylor Van Riper"
105
+ width="2848"
106
+ height="4272"
107
+ />
108
+ ```
109
+
110
+ becomes
111
+
112
+ ```html
113
+ <img
114
+ src="https://images.unsplash.com/photo-1513002749550-c59d786b8e6c?ixlib=rb-4.0.3&amp;ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
115
+ alt="Clouds in the sky by Taylor Van Riper"
116
+ width="2848"
117
+ height="4272"
118
+ loading="lazy"
119
+ decoding="async"
120
+ srcset="
121
+ https://images.unsplash.com/photo-1513002749550-c59d786b8e6c?ixlib=rb-4.0.3&amp;ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&amp;w=2848&amp;fit=min&amp;auto=format 2848w,
122
+ https://images.unsplash.com/photo-1513002749550-c59d786b8e6c?ixlib=rb-4.0.3&amp;ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&amp;w=2548&amp;fit=min&amp;auto=format 2548w,
123
+ https://images.unsplash.com/photo-1513002749550-c59d786b8e6c?ixlib=rb-4.0.3&amp;ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&amp;w=2248&amp;fit=min&amp;auto=format 2248w,
124
+ https://images.unsplash.com/photo-1513002749550-c59d786b8e6c?ixlib=rb-4.0.3&amp;ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&amp;w=1948&amp;fit=min&amp;auto=format 1948w,
125
+ https://images.unsplash.com/photo-1513002749550-c59d786b8e6c?ixlib=rb-4.0.3&amp;ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&amp;w=1648&amp;fit=min&amp;auto=format 1648w,
126
+ https://images.unsplash.com/photo-1513002749550-c59d786b8e6c?ixlib=rb-4.0.3&amp;ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&amp;w=1348&amp;fit=min&amp;auto=format 1348w,
127
+ https://images.unsplash.com/photo-1513002749550-c59d786b8e6c?ixlib=rb-4.0.3&amp;ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&amp;w=1048&amp;fit=min&amp;auto=format 1048w
128
+ "
129
+ sizes="100vw"
130
+ />
131
+ ```
132
+
133
+ [Read more](/features/optimize-images-cdn)
134
+
135
+ ### External images can be downloaded and optimized
136
+
137
+ ```html
138
+ <img
139
+ src="https://images.unsplash.com/photo-1513002749550-c59d786b8e6c?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=jpg&fit=crop&w=1287&q=80"
140
+ alt="Clouds in the sky by Taylor Van Riper"
141
+ />
142
+ ```
143
+
144
+ becomes
145
+
146
+ ```html
147
+ <img
148
+ src="_jampack/ab99b9d280ce4cf7cfc810b59f3a7739.jpg.webp"
149
+ alt="Clouds in the sky by Taylor Van Riper"
150
+ loading="lazy"
151
+ decoding="async"
152
+ width="1287"
153
+ height="1931"
154
+ srcset="
155
+ _jampack/ab99b9d280ce4cf7cfc810b59f3a7739.jpg.webp 1287w,
156
+ _jampack/ab99b9d280ce4cf7cfc810b59f3a7739@987w.webp 987w,
157
+ _jampack/ab99b9d280ce4cf7cfc810b59f3a7739@687w.webp 687w
158
+ "
159
+ sizes="100vw"
160
+ />
161
+ ```
162
+
163
+ [Read more](/features/optimize-images-external)
164
+
97
165
  ### Above & below-the-fold
98
166
 
99
167
  `jampack` optimizes assets above-the-fold ⬆️.
@@ -106,21 +174,21 @@ Lazy-load assets below-the-fold ⬇️.
106
174
 
107
175
  - Images and Iframes are lazy loaded.
108
176
 
109
- See [Above-the-fold optimisations](https://jampack.divriots.com/features/optimize-above-the-fold/) for details.
177
+ [Read more](https://jampack.divriots.com/features/optimize-above-the-fold/)
110
178
 
111
179
  ## Inline critical CSS
112
180
 
113
181
  Avoid [FOUC](https://en.wikipedia.org/wiki/Flash_of_unstyled_content) while stylesheets are downloaded and parsed.
114
182
  `jampack` will inline critical CSS and lazy-load the rest of CSS.
115
183
 
116
- See [Inline critical CSS](https://jampack.divriots.com/features/inline-critical-css/) for details.
184
+ [Read more](https://jampack.divriots.com/features/inline-critical-css/)
117
185
 
118
186
  ## Prefetch links
119
187
 
120
188
  Speedup the future page navigations by prefetching the links on the page.
121
189
  Thanks to [quicklink](https://github.com/GoogleChromeLabs/quicklink), this can be done dynamically as links enter the viewport.
122
190
 
123
- See [Prefetch links](https://jampack.divriots.com/features/prefetch-links/) for details.
191
+ [Read more](https://jampack.divriots.com/features/prefetch-links/)
124
192
 
125
193
  ### All assets are compressed
126
194
 
@@ -137,9 +205,8 @@ In a 2nd PASS, `jampack` compresses all untouched assets and keep the same name
137
205
  | `.webp` | [`sharp`](https://sharp.pixelplumbing.com/) |
138
206
  | `.avif` | [`sharp`](https://sharp.pixelplumbing.com/) |
139
207
 
140
- ### And more!
208
+ ### [And a lot more!](https://jampack.divriots.com/)
141
209
 
142
- See [Documentation](https://jampack.divriots.com/) for all feature list and examples.
143
210
 
144
211
  ## Quick use
145
212
 
@@ -163,7 +230,7 @@ For more options see [CLI options](https://jampack.divriots.com/cli-options/).
163
230
  - [bayjs.org](https://bayjs.org/)
164
231
  - [qwind.pages.dev](https://qwind.pages.dev/)
165
232
  - [Bloycey's Blog](https://bloycey.blog/)
166
- - [codenanshu.in](https://codenanshu.in/)
233
+ - [gxanshu.in](https://gxanshu.in/)
167
234
 
168
235
  > Add yours here
169
236
 
@@ -1,2 +1,4 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
+ import { Options } from '../config-types.js';
2
3
  export declare function compressCSS(originalCode: Buffer, type?: 'inline' | undefined): Promise<Buffer>;
4
+ export declare function loadConfig(config: Options): void;
@@ -1,4 +1,6 @@
1
- import { transform as lightcss, transformStyleAttribute as lightcssStyleAttribute, } from 'lightningcss';
1
+ import browserslist from 'browserslist';
2
+ import { browserslistToTargets, transform as lightcss, transformStyleAttribute as lightcssStyleAttribute, } from 'lightningcss';
3
+ let targets = browserslistToTargets(browserslist('defaults'));
2
4
  export async function compressCSS(originalCode, type) {
3
5
  // Compress with lightningcss
4
6
  let lightCSSData = undefined;
@@ -12,7 +14,11 @@ export async function compressCSS(originalCode, type) {
12
14
  lightCSSData = lightcssStyleAttribute(options).code;
13
15
  }
14
16
  else {
15
- lightCSSData = lightcss({ filename: 'style.css', ...options }).code;
17
+ lightCSSData = lightcss({
18
+ filename: 'style.css',
19
+ targets,
20
+ ...options,
21
+ }).code;
16
22
  }
17
23
  }
18
24
  catch (e) {
@@ -26,3 +32,6 @@ export async function compressCSS(originalCode, type) {
26
32
  }
27
33
  return resultBuffer || originalCode;
28
34
  }
35
+ export function loadConfig(config) {
36
+ targets = browserslistToTargets(browserslist(config.css.browserslist || config.general.browserslist));
37
+ }
@@ -1,6 +1,7 @@
1
1
  import { minify } from 'html-minifier-terser';
2
2
  import { compressCSS } from './css.js';
3
3
  import { compressJS } from './js.js';
4
+ import config from '../config.js';
4
5
  async function minifyJSinHTML(originalCode) {
5
6
  const newCode = await compressJS(originalCode);
6
7
  if (newCode && newCode.length < originalCode.length)
@@ -22,7 +23,7 @@ export async function compressHTML(originalCode) {
22
23
  minifyCSS: minifyCSSinHTML,
23
24
  minifyJS: minifyJSinHTML,
24
25
  sortClassName: true,
25
- sortAttributes: true,
26
+ sortAttributes: config.html.sort_attributes,
26
27
  });
27
28
  if (newhtmlData)
28
29
  return Buffer.from(newhtmlData, 'utf-8');
@@ -1,6 +1,10 @@
1
1
  const default_options = {
2
+ general: {
3
+ browserslist: 'defaults', // defaults = '> 0.5%, last 2 versions, Firefox ESR, not dead'
4
+ },
2
5
  html: {
3
6
  add_css_reset_as: 'inline',
7
+ sort_attributes: false,
4
8
  },
5
9
  css: {
6
10
  inline_critical_css: false,
@@ -10,8 +14,8 @@ const default_options = {
10
14
  },
11
15
  image: {
12
16
  embed_size: 1500,
13
- srcset_min_width: 390 * 2,
14
- srcset_max_width: 1920 * 2,
17
+ srcset_min_width: 390 * 2, // HiDPI phone
18
+ srcset_max_width: 1920 * 2, // 4K
15
19
  max_width: 99999,
16
20
  external: {
17
21
  process: 'off',
@@ -4,14 +4,19 @@ export type WebpOptions = {
4
4
  quality: number;
5
5
  };
6
6
  export type Options = {
7
+ general: {
8
+ browserslist: string;
9
+ };
7
10
  html: {
8
11
  add_css_reset_as: 'inline' | 'off';
12
+ sort_attributes: boolean;
9
13
  };
10
14
  js: {
11
15
  compressor: 'esbuild' | 'swc';
12
16
  };
13
17
  css: {
14
18
  inline_critical_css: boolean;
19
+ browserslist?: string;
15
20
  };
16
21
  image: {
17
22
  embed_size: number;
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import { optimize } from './optimize.js';
5
5
  import $state from './state.js';
6
6
  import { table } from 'table';
7
7
  import { formatBytes } from './utils.js';
8
- import { fast, loadConfig } from './config.js';
8
+ import config, { fast, loadConfig } from './config.js';
9
9
  import { printTitle } from './logger.js';
10
10
  import { exit } from 'process';
11
11
  import kleur from 'kleur';
@@ -13,6 +13,7 @@ import { cleanCache } from './cache.js';
13
13
  import { VERSION } from './packagejson.js';
14
14
  import { mkdirSync } from 'fs';
15
15
  import { join } from 'path';
16
+ import { loadConfig as loadConfigCSS } from './compressors/css.js';
16
17
  const logo = ` __ __
17
18
  |__|____ _____ ___________ ____ | | __
18
19
  | \\__ \\ / \\\\____ \\__ \\ _/ ___\\| |/ /
@@ -67,6 +68,8 @@ program
67
68
  console.error('Failed to create `_jampack` folder.');
68
69
  exit(1);
69
70
  }
71
+ // Inject config into modules
72
+ loadConfigCSS(config);
70
73
  if (!options.onlycomp) {
71
74
  printTitle('PASS 1 - Optimizing');
72
75
  console.time('Done');
package/dist/optimize.js CHANGED
@@ -26,9 +26,10 @@ function getIntAttr(img, attr) {
26
26
  return parsed;
27
27
  }
28
28
  async function analyse(file) {
29
- var _a;
30
29
  console.log('▶ ' + file);
31
30
  const html = (await fs.readFile(path.join($state.dir, file))).toString();
31
+ if (!html.includes('<html'))
32
+ return;
32
33
  const $ = cheerio.load(html, { sourceCodeLocationInfo: true });
33
34
  const theFold = getTheFold($);
34
35
  const imgs = $('img');
@@ -100,22 +101,44 @@ async function analyse(file) {
100
101
  //
101
102
  let prependToHead = '';
102
103
  let appendToHead = '';
104
+ const heads = $('head'); // always exists because doc loaded with isDocument=true (default)
105
+ const charsetElements$ = heads.find('meta[charset]');
106
+ const wasCharsetFirst = !charsetElements$.prev().length;
103
107
  if (config.html.add_css_reset_as === 'inline') {
104
108
  prependToHead += `<style>:where(img){height:auto;}</style>`;
105
109
  }
106
110
  if (prependToHead || appendToHead) {
107
- let heads = $('head');
108
- if (heads.length === 0) {
109
- // head tag doesn't exist
110
- (_a = $('html')) === null || _a === void 0 ? void 0 : _a.prepend(`<head></head>`);
111
- }
112
- heads = $('head');
113
- // head tag exist
114
111
  if (prependToHead)
115
112
  heads.prepend(prependToHead);
116
113
  if (appendToHead)
117
114
  heads.append(appendToHead);
118
115
  }
116
+ switch (charsetElements$.length) {
117
+ case 0:
118
+ $state.reportIssue(file, {
119
+ type: 'fix',
120
+ msg: 'Adding missing <meta charset="utf-8"> to the top of the <head>',
121
+ });
122
+ heads.prepend('<meta charset="utf-8">');
123
+ break;
124
+ case 1:
125
+ if (!wasCharsetFirst) {
126
+ $state.reportIssue(file, {
127
+ type: 'perf',
128
+ msg: 'Moving <meta charset> to the top of the <head>',
129
+ });
130
+ }
131
+ if (!wasCharsetFirst || prependToHead)
132
+ heads.prepend(charsetElements$);
133
+ break;
134
+ default:
135
+ $state.reportIssue(file, {
136
+ type: 'fix',
137
+ msg: 'Multiple <meta charset> found in the <head>: taking only the first one and deleting the others',
138
+ });
139
+ charsetElements$.remove();
140
+ heads.prepend(charsetElements$.first());
141
+ }
119
142
  // Add to <body>
120
143
  //
121
144
  let appendToBody = '';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@divriots/jampack",
3
- "version": "0.22.1",
3
+ "version": "0.23.0",
4
4
  "cache-version": {
5
5
  "img": "v1",
6
6
  "img-ext": "v1"
@@ -28,34 +28,35 @@
28
28
  "dependencies": {
29
29
  "@divriots/cheerio": "1.0.0-rc.12",
30
30
  "@proload/core": "^0.3.3",
31
- "@swc/core": "^1.3.86",
31
+ "@swc/core": "^1.3.100",
32
32
  "add": "^2.0.6",
33
+ "browserslist": "^4.22.2",
33
34
  "commander": "^10.0.1",
34
35
  "critters": "^0.0.20",
35
36
  "deepmerge": "^4.3.1",
36
- "esbuild": "^0.19.3",
37
- "file-type": "^18.5.0",
37
+ "esbuild": "^0.19.9",
38
+ "file-type": "^18.7.0",
38
39
  "globby": "^13.2.2",
39
40
  "hasha": "^5.2.2",
40
41
  "html-minifier-terser": "^7.2.0",
41
42
  "kleur": "^4.1.5",
42
- "lightningcss": "^1.22.0",
43
+ "lightningcss": "^1.22.1",
43
44
  "mini-svg-data-uri": "^1.4.4",
44
45
  "ora": "^7.0.1",
45
46
  "quicklink": "^2.3.0",
46
- "sharp": "^0.32.6",
47
- "svgo": "^3.0.2",
47
+ "sharp": "^0.33.0",
48
+ "svgo": "^3.1.0",
48
49
  "table": "^6.8.1",
49
- "unpic": "^3.11.0",
50
- "undici": "^5.24.0"
50
+ "undici": "^5.24.0",
51
+ "unpic": "^3.16.0"
51
52
  },
52
53
  "devDependencies": {
53
- "@types/html-minifier-terser": "^7.0.0",
54
- "@types/node": "^20.6.2",
54
+ "@types/html-minifier-terser": "^7.0.2",
55
+ "@types/node": "^20.10.4",
55
56
  "shx": "^0.3.3",
56
- "ts-node": "^10.2.1",
57
+ "ts-node": "^10.9.2",
57
58
  "tslib": "^2.6.2",
58
- "typescript": "^5.2.2"
59
+ "typescript": "^5.3.3"
59
60
  },
60
61
  "scripts": {
61
62
  "watch": "pnpm build --watch",