@divriots/jampack 0.15.0 → 0.20.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
@@ -116,7 +116,7 @@ In a 2nd PASS, `jampack` compresses all untouched assets and keep the same name
116
116
  | --------------- | --------------------- |
117
117
  | `.html`,`.htm` | [`html-minifier-terser`](https://github.com/terser/html-minifier-terser) |
118
118
  | `.css` | [`lightningCSS`](https://lightningcss.dev) |
119
- | `.js` | [`swc`](https://swc.rs/) |
119
+ | `.js` | [`esbuild`](https://esbuild.github.io/) or [`swc`](https://swc.rs/) |
120
120
  | `.svg` | [`svgo`](https://github.com/svg/svgo) |
121
121
  | `.jpg`,`.jpeg` | [`sharp`](https://sharp.pixelplumbing.com/) |
122
122
  | `.png` | [`sharp`](https://sharp.pixelplumbing.com/) |
@@ -1,8 +1,20 @@
1
1
  import swc from '@swc/core';
2
+ import * as esbuild from 'esbuild';
3
+ import config from '../config.js';
2
4
  export async function compressJS(originalCode) {
3
- const newjsresult = await swc.minify(originalCode, {
4
- compress: true,
5
- mangle: true,
6
- });
7
- return newjsresult.code;
5
+ let resultCode = originalCode;
6
+ switch (config.js.compressor) {
7
+ case 'esbuild':
8
+ resultCode = (await esbuild.transform(originalCode, {
9
+ minify: true,
10
+ })).code;
11
+ break;
12
+ case 'swc':
13
+ resultCode = (await swc.minify(originalCode, {
14
+ compress: true,
15
+ mangle: true,
16
+ })).code;
17
+ break;
18
+ }
19
+ return resultCode;
8
20
  }
@@ -2,9 +2,15 @@ const default_options = {
2
2
  html: {
3
3
  add_css_reset_as: 'inline',
4
4
  },
5
+ css: {
6
+ inline_critical_css: false, // Doesn't work yet
7
+ },
8
+ js: {
9
+ compressor: 'esbuild',
10
+ },
5
11
  image: {
6
12
  embed_size: 1500,
7
- srcset_min_width: 640,
13
+ srcset_min_width: 390 * 2,
8
14
  srcset_max_width: 1920 * 2,
9
15
  max_width: 99999,
10
16
  external: {
@@ -40,5 +46,8 @@ const default_options = {
40
46
  optimization: true,
41
47
  },
42
48
  },
49
+ misc: {
50
+ prefetch_links: 'off',
51
+ },
43
52
  };
44
53
  export default default_options;
@@ -1,7 +1,7 @@
1
1
  const fast_options_override = {
2
2
  image: {
3
3
  embed_size: 0,
4
- srcset_min_width: 16000,
4
+ srcset_min_width: 99999,
5
5
  compress: false,
6
6
  jpeg: {
7
7
  options: {
@@ -25,5 +25,8 @@ const fast_options_override = {
25
25
  optimization: false,
26
26
  },
27
27
  },
28
+ misc: {
29
+ prefetch_links: 'none',
30
+ },
28
31
  };
29
32
  export default fast_options_override;
@@ -7,6 +7,12 @@ export type Options = {
7
7
  html: {
8
8
  add_css_reset_as: 'inline' | 'off';
9
9
  };
10
+ js: {
11
+ compressor: 'esbuild' | 'swc';
12
+ };
13
+ css: {
14
+ inline_critical_css: boolean;
15
+ };
10
16
  image: {
11
17
  embed_size: number;
12
18
  srcset_min_width: number;
@@ -37,4 +43,7 @@ export type Options = {
37
43
  optimization: boolean;
38
44
  };
39
45
  };
46
+ misc: {
47
+ prefetch_links: 'in-viewport' | 'off';
48
+ };
40
49
  };
package/dist/optimize.js CHANGED
@@ -11,6 +11,8 @@ import kleur from 'kleur';
11
11
  import ora from 'ora';
12
12
  import { isLocal, Resource, translateSrc } from './utils/resource.js';
13
13
  import { downloadExternalImage } from './optimizers/img-external.js';
14
+ import { inlineCriticalCss } from './optimizers/inline-critical-css.js';
15
+ import { prefetch_links_in_viewport } from './optimizers/prefetch-links.js';
14
16
  async function analyse(file) {
15
17
  var _a;
16
18
  console.log('▶ ' + file);
@@ -76,22 +78,58 @@ async function analyse(file) {
76
78
  spinnerIframe.text = kleur.dim(`<iframe> [${imgsArray.length}/${imgsArray.length}]`);
77
79
  spinnerIframe.succeed();
78
80
  // Remove the fold
81
+ //
79
82
  if (theFold) {
80
83
  $('the-fold').remove();
81
84
  }
82
- // Add CSS to head
85
+ // Add to <head>
86
+ //
87
+ let prependToHead = '';
88
+ let appendToHead = '';
83
89
  if (config.html.add_css_reset_as === 'inline') {
84
- const CSS = `<style>:where(img){height:auto;}</style>`;
85
- const heads = $('head');
86
- if (heads.length > 0) {
87
- heads.prepend(CSS);
90
+ prependToHead += `<style>:where(img){height:auto;}</style>`;
91
+ }
92
+ if (prependToHead || appendToHead) {
93
+ let heads = $('head');
94
+ if (heads.length === 0) {
95
+ // head tag doesn't exist
96
+ (_a = $('html')) === null || _a === void 0 ? void 0 : _a.prepend(`<head></head>`);
88
97
  }
89
- else {
90
- (_a = $('html')) === null || _a === void 0 ? void 0 : _a.prepend(`<head>${CSS}</head>`);
98
+ heads = $('head');
99
+ // head tag exist
100
+ if (prependToHead)
101
+ heads.prepend(prependToHead);
102
+ if (appendToHead)
103
+ heads.append(appendToHead);
104
+ }
105
+ // Add to <body>
106
+ //
107
+ let appendToBody = '';
108
+ if (config.misc.prefetch_links === 'in-viewport') {
109
+ appendToBody += await prefetch_links_in_viewport(file);
110
+ }
111
+ if (appendToBody) {
112
+ const body = $('body');
113
+ if (body.length > 0) {
114
+ body.append(appendToBody);
115
+ }
116
+ }
117
+ // Render HTML
118
+ //
119
+ let htmlResult = $.html();
120
+ // Inline critical css
121
+ //
122
+ if (config.css.inline_critical_css) {
123
+ try {
124
+ const location = path.resolve(path.dirname(path.join($state.dir, file)));
125
+ htmlResult = await inlineCriticalCss(location, htmlResult);
126
+ }
127
+ catch (e) {
128
+ console.warn('Fail to inline critical CSS', e);
91
129
  }
92
130
  }
93
131
  if (!$state.args.nowrite) {
94
- await fs.writeFile(path.join($state.dir, file), $.html());
132
+ await fs.writeFile(path.join($state.dir, file), htmlResult);
95
133
  }
96
134
  }
97
135
  function getTheFold($) {
@@ -0,0 +1 @@
1
+ export declare function inlineCriticalCss(path: string, html: string): any;
@@ -0,0 +1,11 @@
1
+ // @ts-ignore
2
+ import Critters from 'critters';
3
+ export function inlineCriticalCss(path, html) {
4
+ const critters = new Critters({
5
+ compress: false,
6
+ fonts: false,
7
+ logLevel: 'info',
8
+ path,
9
+ });
10
+ return critters.process(html);
11
+ }
@@ -0,0 +1 @@
1
+ export declare function prefetch_links_in_viewport(html_file: string): Promise<string>;
@@ -0,0 +1,26 @@
1
+ import $state from '../state.js';
2
+ import * as fs from 'fs/promises';
3
+ import * as path from 'path';
4
+ import * as url from 'url';
5
+ const src_filename = 'quicklink.mjs';
6
+ const folder = `/_jampack/quicklink-2.3.0`;
7
+ const url_loader = `${folder}/loader.js`;
8
+ const url_quicklink = `${folder}/${src_filename}`;
9
+ let payloadStored = false;
10
+ export async function prefetch_links_in_viewport(html_file) {
11
+ const path_html = path.dirname('/' + html_file);
12
+ if (!payloadStored) {
13
+ const path_loader = path.join($state.dir, url_loader);
14
+ await fs.mkdir(path.join($state.dir, folder), { recursive: true });
15
+ // Write loader
16
+ const code_loader = `import { listen } from "./quicklink.mjs";
17
+ listen();`;
18
+ await fs.writeFile(path_loader, code_loader);
19
+ // Write quicklink code
20
+ const source = path.join(path.dirname(url.fileURLToPath(import.meta.url)), `../../node_modules/quicklink/dist/${src_filename}`);
21
+ await fs.copyFile(source, path.join($state.dir, folder, src_filename));
22
+ // We don't need to store them anymore
23
+ payloadStored = true;
24
+ }
25
+ return `<script type="module" src="${path.relative(path_html, url_loader)}"></script>`;
26
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@divriots/jampack",
3
- "version": "0.15.0",
3
+ "version": "0.20.0",
4
4
  "cache-version": {
5
5
  "img": "v1",
6
6
  "img-ext": "v1"
@@ -22,9 +22,12 @@
22
22
  "dependencies": {
23
23
  "@divriots/cheerio": "1.0.0-rc.12",
24
24
  "@proload/core": "^0.3.3",
25
- "@swc/core": "^1.3.80",
25
+ "@swc/core": "^1.3.82",
26
+ "add": "^2.0.6",
26
27
  "commander": "^10.0.1",
28
+ "critters": "^0.0.20",
27
29
  "deepmerge": "^4.3.1",
30
+ "esbuild": "^0.19.2",
28
31
  "file-type": "^18.5.0",
29
32
  "globby": "^13.2.2",
30
33
  "hasha": "^5.2.2",
@@ -33,6 +36,7 @@
33
36
  "lightningcss": "^1.21.5",
34
37
  "mini-svg-data-uri": "^1.4.4",
35
38
  "ora": "^7.0.1",
39
+ "quicklink": "^2.3.0",
36
40
  "sharp": "^0.32.5",
37
41
  "svgo": "^3.0.2",
38
42
  "table": "^6.8.1",
@@ -48,7 +52,7 @@
48
52
  },
49
53
  "scripts": {
50
54
  "watch": "pnpm build --watch",
51
- "try": "shx rm -fr demo && cp -R demo_rf demo && node ./dist/index.js ./demo --nocache --onlyoptim",
55
+ "try": "shx rm -fr demo && cp -R demo_ref demo && node ./dist/index.js ./demo --nocache",
52
56
  "build": "shx rm -rf dist && tsc -b",
53
57
  "lint": "eslint . --ext .ts --config .eslintrc",
54
58
  "prepublishOnly": "pnpm build",