@divriots/jampack 0.24.3 → 0.25.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/dist/cache.d.ts CHANGED
@@ -1,12 +1,13 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
+ import { GlobalState } from './state.js';
2
3
  declare const listOfCategories: readonly ["img", "img-ext"];
3
4
  export type Category = (typeof listOfCategories)[number];
4
5
  export type CacheData = {
5
6
  buffer: Buffer;
6
7
  meta: any;
7
8
  };
8
- declare function cleanCache(full: boolean): Promise<void>;
9
- declare function computeCacheHash(buffer: Buffer, options?: any): string;
10
- declare function getFromCache(category: Category, hash: string): Promise<CacheData | undefined>;
11
- declare function addToCache(category: Category, hash: string, data: CacheData): Promise<void>;
9
+ declare function cleanCache(state: GlobalState, full?: boolean): Promise<void>;
10
+ declare function computeCacheHash(state: GlobalState, buffer: Buffer, options?: any): string;
11
+ declare function getFromCache(state: GlobalState, category: Category, hash: string): Promise<CacheData | undefined>;
12
+ declare function addToCache(state: GlobalState, category: Category, hash: string, data: CacheData): Promise<void>;
12
13
  export { cleanCache, computeCacheHash, getFromCache, addToCache };
package/dist/cache.js CHANGED
@@ -1,14 +1,13 @@
1
1
  import { hashSync as hasha } from 'hasha';
2
2
  import path from 'path';
3
3
  import * as fs from 'fs/promises';
4
- import $state from './state.js';
5
4
  import { CACHE_VERSIONS } from './packagejson.js';
6
5
  const listOfCategories = ['img', 'img-ext'];
7
- function getCacheFolder() {
8
- return $state.args.cache_folder || '.jampack/cache';
6
+ function getCacheFolder(state) {
7
+ return state.args.cache_folder || '.jampack/cache';
9
8
  }
10
- async function cleanCache(full) {
11
- const CACHE_FOLDER = getCacheFolder();
9
+ async function cleanCache(state, full) {
10
+ const CACHE_FOLDER = getCacheFolder(state);
12
11
  if (full) {
13
12
  try {
14
13
  await fs.rm(CACHE_FOLDER, { recursive: true });
@@ -49,8 +48,8 @@ async function cleanCache(full) {
49
48
  }
50
49
  }
51
50
  }
52
- function computeCacheHash(buffer, options) {
53
- if ($state.args.nocache) {
51
+ function computeCacheHash(state, buffer, options) {
52
+ if (state.args.nocache) {
54
53
  return '';
55
54
  }
56
55
  let hash = `${hasha(buffer, { algorithm: 'sha256' })}`;
@@ -62,15 +61,15 @@ function computeCacheHash(buffer, options) {
62
61
  function getVersionOfCategory(category) {
63
62
  return CACHE_VERSIONS[category];
64
63
  }
65
- function getLocation(hash, category) {
66
- const CACHE_FOLDER = getCacheFolder();
64
+ function getLocation(state, hash, category) {
65
+ const CACHE_FOLDER = getCacheFolder(state);
67
66
  return path.join(CACHE_FOLDER, category, getVersionOfCategory(category), hash);
68
67
  }
69
- async function getFromCache(category, hash) {
70
- if ($state.args.nocache) {
68
+ async function getFromCache(state, category, hash) {
69
+ if (state.args.nocache) {
71
70
  return undefined;
72
71
  }
73
- const location = getLocation(hash, category);
72
+ const location = getLocation(state, hash, category);
74
73
  try {
75
74
  const buffer = await fs.readFile(path.join(location, 'data'));
76
75
  const meta = JSON.parse((await fs.readFile(path.join(location, 'meta'))).toString());
@@ -81,11 +80,11 @@ async function getFromCache(category, hash) {
81
80
  }
82
81
  return undefined;
83
82
  }
84
- async function addToCache(category, hash, data) {
85
- if ($state.args.nocache) {
83
+ async function addToCache(state, category, hash, data) {
84
+ if (state.args.nocache) {
86
85
  return;
87
86
  }
88
- const location = getLocation(hash, category);
87
+ const location = getLocation(state, hash, category);
89
88
  await fs.mkdir(location, { recursive: true });
90
89
  await fs.writeFile(path.join(location, 'data'), data.buffer);
91
90
  await fs.writeFile(path.join(location, 'meta'), JSON.stringify(data.meta));
@@ -1 +1,2 @@
1
- export declare function compressFolder(exclude: string): Promise<void>;
1
+ import { GlobalState } from './state.js';
2
+ export declare function compressFolder(state: GlobalState, exclude?: string): Promise<void>;
package/dist/compress.js CHANGED
@@ -1,15 +1,13 @@
1
1
  import * as fs from 'fs/promises';
2
2
  import * as path from 'path';
3
3
  import { formatBytes } from './utils.js';
4
- import config from './config.js';
5
- import $state from './state.js';
6
4
  import { globby } from 'globby';
7
5
  import ora from 'ora';
8
6
  import { compressCSS } from './compressors/css.js';
9
7
  import { compressJS } from './compressors/js.js';
10
8
  import { compressHTML } from './compressors/html.js';
11
9
  import { compressImage } from './compressors/images.js';
12
- const processFile = async (file, stats) => {
10
+ const processFile = async (state, file, stats) => {
13
11
  let writeData = undefined;
14
12
  try {
15
13
  const ext = path.extname(file);
@@ -20,9 +18,9 @@ const processFile = async (file, stats) => {
20
18
  case '.svg':
21
19
  case '.webp':
22
20
  case '.avif':
23
- if (config.image.compress) {
21
+ if (state.options.image.compress) {
24
22
  const imgData = await fs.readFile(file);
25
- const newImage = await compressImage(imgData, {});
23
+ const newImage = await compressImage(state, imgData, {});
26
24
  if ((newImage === null || newImage === void 0 ? void 0 : newImage.data) && newImage.data.length < stats.size) {
27
25
  writeData = newImage.data;
28
26
  }
@@ -31,19 +29,19 @@ const processFile = async (file, stats) => {
31
29
  case '.html':
32
30
  case '.htm':
33
31
  const htmldata = await fs.readFile(file);
34
- const newhtmlData = await compressHTML(htmldata);
32
+ const newhtmlData = await compressHTML(state, htmldata);
35
33
  writeData = newhtmlData;
36
34
  break;
37
35
  case '.css':
38
36
  const cssdata = await fs.readFile(file);
39
- const newCSS = await compressCSS(cssdata);
37
+ const newCSS = await compressCSS(state, cssdata);
40
38
  if (newCSS && newCSS.length < cssdata.length) {
41
39
  writeData = newCSS;
42
40
  }
43
41
  break;
44
42
  case '.js':
45
43
  const jsdata = await fs.readFile(file);
46
- const newJS = await compressJS(jsdata.toString());
44
+ const newJS = await compressJS(state, jsdata.toString());
47
45
  if (newJS && newJS.length < jsdata.length) {
48
46
  writeData = newJS;
49
47
  }
@@ -63,30 +61,30 @@ const processFile = async (file, stats) => {
63
61
  // Writedata
64
62
  if (writeData && writeData.length < result.originalSize) {
65
63
  result.compressedSize = writeData.length;
66
- if (!$state.args.nowrite) {
64
+ if (!state.args.nowrite) {
67
65
  await fs.writeFile(file, writeData);
68
66
  }
69
67
  }
70
- $state.compressedFiles.add(file);
71
- $state.reportSummary(result);
68
+ state.compressedFiles.add(file);
69
+ state.reportSummary(result);
72
70
  };
73
- export async function compressFolder(exclude) {
74
- const spinner = ora(getProgressText()).start();
71
+ export async function compressFolder(state, exclude) {
72
+ const spinner = ora(getProgressText(state)).start();
75
73
  const globs = ['**/**', '!_jampack/**']; // Exclude jampack folder because already compressed
76
74
  if (exclude)
77
75
  globs.push('!' + exclude);
78
- const paths = await globby(globs, { cwd: $state.dir, absolute: true });
76
+ const paths = await globby(globs, { cwd: state.dir, absolute: true });
79
77
  // "Parallel" processing
80
78
  await Promise.all(paths.map(async (file) => {
81
- if (!$state.compressedFiles.has(file)) {
82
- await processFile(file, await fs.stat(file));
83
- spinner.text = getProgressText();
79
+ if (!state.compressedFiles.has(file)) {
80
+ await processFile(state, file, await fs.stat(file));
81
+ spinner.text = getProgressText(state);
84
82
  }
85
83
  }));
86
- spinner.text = getProgressText();
84
+ spinner.text = getProgressText(state);
87
85
  spinner.succeed();
88
86
  }
89
- const getProgressText = () => {
90
- const gain = $state.summary.dataLenUncompressed - $state.summary.dataLenCompressed;
91
- return `${$state.summary.nbFiles} files | ${formatBytes($state.summary.dataLenUncompressed)} → ${formatBytes($state.summary.dataLenCompressed)} | -${formatBytes(gain)} `;
87
+ const getProgressText = (state) => {
88
+ const gain = state.summary.dataLenUncompressed - state.summary.dataLenCompressed;
89
+ return `${state.summary.nbFiles} files | ${formatBytes(state.summary.dataLenUncompressed)} → ${formatBytes(state.summary.dataLenCompressed)} | -${formatBytes(gain)} `;
92
90
  };
@@ -1,4 +1,5 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
- import { Options } from '../config-types.js';
3
- export declare function compressCSS(originalCode: Buffer, type?: 'inline' | undefined): Promise<Buffer>;
4
- export declare function loadConfig(config: Options): void;
2
+ import { GlobalState } from '../state.js';
3
+ export declare const defaultTargets: () => import("lightningcss").Targets;
4
+ export declare function compressCSS({ targets }: GlobalState, originalCode: Buffer, type?: 'inline' | undefined): Promise<Buffer>;
5
+ export declare function loadConfigCSS(state: GlobalState): void;
@@ -1,7 +1,7 @@
1
1
  import browserslist from 'browserslist';
2
2
  import { browserslistToTargets, transform as lightcss, transformStyleAttribute as lightcssStyleAttribute, } from 'lightningcss';
3
- let targets = browserslistToTargets(browserslist('defaults'));
4
- export async function compressCSS(originalCode, type) {
3
+ export const defaultTargets = () => browserslistToTargets(browserslist('defaults'));
4
+ export async function compressCSS({ targets }, originalCode, type) {
5
5
  // Compress with lightningcss
6
6
  let lightCSSData = undefined;
7
7
  try {
@@ -28,10 +28,10 @@ export async function compressCSS(originalCode, type) {
28
28
  }
29
29
  let resultBuffer = undefined;
30
30
  if (lightCSSData && lightCSSData.length < originalCode.length) {
31
- resultBuffer = lightCSSData;
31
+ resultBuffer = Buffer.from(lightCSSData);
32
32
  }
33
33
  return resultBuffer || originalCode;
34
34
  }
35
- export function loadConfig(config) {
36
- targets = browserslistToTargets(browserslist(config.css.browserslist || config.general.browserslist));
35
+ export function loadConfigCSS(state) {
36
+ const { options } = state;
37
37
  }
@@ -1,2 +1,3 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
- export declare function compressHTML(originalCode: Buffer): Promise<Buffer>;
2
+ import { GlobalState } from '../state.js';
3
+ export declare function compressHTML(state: GlobalState, originalCode: Buffer): Promise<Buffer>;
@@ -1,29 +1,28 @@
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';
5
- async function minifyJSinHTML(originalCode) {
6
- const newCode = await compressJS(originalCode);
4
+ async function minifyJSinHTML(state, originalCode) {
5
+ const newCode = await compressJS(state, originalCode);
7
6
  if (newCode && newCode.length < originalCode.length)
8
7
  return newCode;
9
8
  return originalCode;
10
9
  }
11
- async function minifyCSSinHTML(originalCode, type) {
10
+ async function minifyCSSinHTML(state, originalCode, type) {
12
11
  // Don't compress media
13
- if (type === 'media')
12
+ if (type !== undefined && type !== 'inline')
14
13
  return originalCode;
15
14
  const originalBuffer = Buffer.from(originalCode);
16
- const newCSS = await compressCSS(originalBuffer, type);
15
+ const newCSS = await compressCSS(state, originalBuffer, type);
17
16
  if (newCSS && newCSS.length > 0 && newCSS.length < originalBuffer.length)
18
17
  return newCSS.toString();
19
18
  return originalCode;
20
19
  }
21
- export async function compressHTML(originalCode) {
20
+ export async function compressHTML(state, originalCode) {
22
21
  const newhtmlData = await minify(originalCode.toString(), {
23
- minifyCSS: minifyCSSinHTML,
24
- minifyJS: minifyJSinHTML,
22
+ minifyCSS: (text, type) => minifyCSSinHTML(state, text, type),
23
+ minifyJS: (text) => minifyJSinHTML(state, text),
25
24
  sortClassName: true,
26
- sortAttributes: config.html.sort_attributes,
25
+ sortAttributes: state.options.html.sort_attributes,
27
26
  });
28
27
  if (newhtmlData)
29
28
  return Buffer.from(newhtmlData, 'utf-8');
@@ -1,6 +1,7 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
2
  import sharp from 'sharp';
3
3
  import { MimeType } from 'file-type';
4
+ import { GlobalState } from '../state.js';
4
5
  export type ImageMimeType = MimeType | 'image/svg+xml';
5
6
  export declare const AllImageFormat: string[];
6
7
  export type ImageFormat = (typeof AllImageFormat)[number] | undefined;
@@ -12,4 +13,4 @@ export type ImageOutputOptions = {
12
13
  resize?: sharp.ResizeOptions;
13
14
  toFormat?: 'webp' | 'avif' | 'png' | 'jpeg' | 'unchanged';
14
15
  };
15
- export declare function compressImage(data: Buffer, options: ImageOutputOptions): Promise<Image | undefined>;
16
+ export declare function compressImage(state: GlobalState, data: Buffer, options: ImageOutputOptions): Promise<Image | undefined>;
@@ -1,7 +1,6 @@
1
1
  import sharp from 'sharp';
2
2
  import { optimize as svgo } from 'svgo';
3
3
  import { getFromCache, addToCache, computeCacheHash } from '../cache.js';
4
- import config from '../config.js';
5
4
  export const AllImageFormat = ['webp', 'svg', 'jpg', 'png', 'avif'];
6
5
  function createWebpOptions(opt) {
7
6
  return {
@@ -10,10 +9,10 @@ function createWebpOptions(opt) {
10
9
  effort: opt.effort,
11
10
  };
12
11
  }
13
- export async function compressImage(data, options) {
12
+ export async function compressImage(state, data, options) {
14
13
  var _a, _b;
15
- const cacheHash = computeCacheHash(data, options);
16
- const imageFromCache = await getFromCache('img', cacheHash);
14
+ const cacheHash = computeCacheHash(state, data, options);
15
+ const imageFromCache = await getFromCache(state, 'img', cacheHash);
17
16
  if (imageFromCache) {
18
17
  return { data: imageFromCache.buffer, format: imageFromCache.meta };
19
18
  }
@@ -27,9 +26,10 @@ export async function compressImage(data, options) {
27
26
  return undefined;
28
27
  }
29
28
  let outputFormat;
29
+ const imageOptions = state.options.image;
30
30
  // Special case for svg
31
31
  if (meta.format === 'svg') {
32
- if (!config.image.svg.optimization)
32
+ if (!imageOptions.svg.optimization)
33
33
  return undefined;
34
34
  try {
35
35
  const output = svgo(data.toString(), {
@@ -60,16 +60,16 @@ export async function compressImage(data, options) {
60
60
  if (toFormat === 'unchanged') {
61
61
  switch (meta.format) {
62
62
  case 'png':
63
- sharpFile = sharpFile.png(config.image.png.options || {});
63
+ sharpFile = sharpFile.png(imageOptions.png.options || {});
64
64
  outputFormat = 'png';
65
65
  break;
66
66
  case 'jpeg':
67
67
  case 'jpg':
68
- sharpFile = sharpFile.jpeg(config.image.jpeg.options || {});
68
+ sharpFile = sharpFile.jpeg(imageOptions.jpeg.options || {});
69
69
  outputFormat = 'jpg';
70
70
  break;
71
71
  case 'webp':
72
- sharpFile = sharpFile.webp(createWebpOptions(config.image.webp.options_lossly) || {});
72
+ sharpFile = sharpFile.webp(createWebpOptions(imageOptions.webp.options_lossly) || {});
73
73
  outputFormat = 'webp';
74
74
  break;
75
75
  case 'heif':
@@ -83,17 +83,17 @@ export async function compressImage(data, options) {
83
83
  // To format
84
84
  switch (toFormat) {
85
85
  case 'jpeg':
86
- sharpFile = sharpFile.jpeg({ ...config.image.jpeg.options } || {});
86
+ sharpFile = sharpFile.jpeg({ ...imageOptions.jpeg.options } || {});
87
87
  outputFormat = 'jpg';
88
88
  break;
89
89
  case 'png':
90
- sharpFile = sharpFile.png({ ...config.image.png.options } || {});
90
+ sharpFile = sharpFile.png({ ...imageOptions.png.options } || {});
91
91
  outputFormat = 'png';
92
92
  break;
93
93
  case 'webp':
94
94
  sharpFile = sharpFile.webp(createWebpOptions(meta.format === 'png'
95
- ? config.image.webp.options_lossless
96
- : config.image.webp.options_lossly));
95
+ ? imageOptions.webp.options_lossless
96
+ : imageOptions.webp.options_lossly));
97
97
  outputFormat = 'webp';
98
98
  break;
99
99
  case 'avif':
@@ -125,7 +125,7 @@ export async function compressImage(data, options) {
125
125
  format: outputFormat,
126
126
  data: await sharpFile.toBuffer(),
127
127
  };
128
- await addToCache('img', cacheHash, {
128
+ await addToCache(state, 'img', cacheHash, {
129
129
  buffer: outputImage.data,
130
130
  meta: outputImage.format,
131
131
  });
@@ -1 +1,2 @@
1
- export declare function compressJS(originalCode: string): Promise<string>;
1
+ import { GlobalState } from '../state.js';
2
+ export declare function compressJS({ options }: GlobalState, originalCode: string): Promise<string>;
@@ -1,9 +1,8 @@
1
1
  import swc from '@swc/core';
2
2
  import * as esbuild from 'esbuild';
3
- import config from '../config.js';
4
- export async function compressJS(originalCode) {
3
+ export async function compressJS({ options }, originalCode) {
5
4
  let resultCode = originalCode;
6
- switch (config.js.compressor) {
5
+ switch (options.js.compressor) {
7
6
  case 'esbuild':
8
7
  resultCode = (await esbuild.transform(originalCode, {
9
8
  minify: true,
@@ -17,6 +17,8 @@ const default_options = {
17
17
  srcset_min_width: 390 * 2, // HiDPI phone
18
18
  srcset_max_width: 1920 * 2, // 4K
19
19
  max_width: 99999,
20
+ src_include: /^.*$/,
21
+ src_exclude: /^\/vercel\/image\?/, // Ignore /vervel/image? URLs because not local and most likely already optimized,
20
22
  external: {
21
23
  process: 'off',
22
24
  src_include: /^.*$/,
@@ -24,6 +24,8 @@ export type Options = {
24
24
  srcset_min_width: number;
25
25
  srcset_max_width: number;
26
26
  max_width: number;
27
+ src_include: RegExp;
28
+ src_exclude: RegExp | null;
27
29
  external: {
28
30
  process: 'off' | 'download';
29
31
  src_include: RegExp;
package/dist/config.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- declare const options: import("./config-types.js").Options;
2
- export declare function fast(): void;
3
- export declare function loadConfig(): Promise<void>;
4
- export default options;
1
+ import { GlobalState } from './state.js';
2
+ export declare function fast(state: GlobalState): void;
3
+ export declare function loadConfig(state: GlobalState): Promise<void>;
package/dist/config.js CHANGED
@@ -3,16 +3,18 @@ import load from '@proload/core';
3
3
  import deepmerge from 'deepmerge';
4
4
  import default_options from './config-default.js';
5
5
  import fast_options_override from './config-fast.js';
6
- const options = default_options;
7
- export function fast() {
8
- Object.assign(options, deepmerge(options, fast_options_override));
6
+ import { loadConfigCSS } from './compressors/css.js';
7
+ export function fast(state) {
8
+ const options = default_options;
9
+ Object.assign(state.options, deepmerge(options, fast_options_override));
9
10
  }
10
- export async function loadConfig() {
11
+ export async function loadConfig(state) {
12
+ const options = default_options;
11
13
  const proload = await load('jampack', { mustExist: false });
12
14
  if (proload) {
13
15
  console.log('Merging default config with:');
14
16
  console.log(JSON.stringify(proload.value, null, 2));
15
- Object.assign(options, deepmerge(options, proload.value));
17
+ Object.assign(state.options, deepmerge(options, proload.value));
16
18
  }
19
+ loadConfigCSS(state);
17
20
  }
18
- export default options;
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import { Command } from 'commander';
2
+ import { Command } from '@commander-js/extra-typings';
3
3
  import { compressFolder } from './compress.js';
4
4
  import { optimize } from './optimize.js';
5
- import $state from './state.js';
5
+ import { GlobalState } from './state.js';
6
6
  import { table } from 'table';
7
7
  import { formatBytes } from './utils.js';
8
- import config, { fast, loadConfig } from './config.js';
8
+ import { 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,7 +13,6 @@ 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';
17
16
  const logo = ` __ __
18
17
  |__|____ _____ ___________ ____ | | __
19
18
  | \\__ \\ / \\\\____ \\__ \\ _/ ___\\| |/ /
@@ -43,9 +42,10 @@ program
43
42
  .option('--cleancache', 'Clean cache before running')
44
43
  .option('--nocache', 'Run with no use of cache')
45
44
  .action(async (dir, options) => {
45
+ const state = new GlobalState();
46
46
  // Arguments
47
- $state.dir = dir;
48
- $state.args = options;
47
+ state.dir = dir;
48
+ state.args = options;
49
49
  // Print options
50
50
  if (options) {
51
51
  console.log('Options:');
@@ -53,43 +53,41 @@ program
53
53
  console.log('');
54
54
  }
55
55
  // Override default config with config file
56
- await loadConfig();
56
+ await loadConfig(state);
57
57
  // Override config with fast options if set
58
58
  if (options.fast) {
59
- fast();
59
+ fast(state);
60
60
  }
61
61
  // Clean cache
62
- await cleanCache(options.cleancache);
62
+ await cleanCache(state, options.cleancache);
63
63
  // Make _jampack folder
64
64
  try {
65
65
  mkdirSync(join(dir, '_jampack'));
66
66
  }
67
67
  catch (e) {
68
- console.error('Failed to create `_jampack` folder.');
68
+ console.error('Folder `_jampack` is present in target folder. This means that jampack has already processed this folder. You should always run jampack on clean build of the static website.');
69
69
  exit(1);
70
70
  }
71
- // Inject config into modules
72
- loadConfigCSS(config);
73
71
  if (!options.onlycomp) {
74
72
  printTitle('PASS 1 - Optimizing');
75
73
  console.time('Done');
76
- await optimize(options.include, options.exclude);
74
+ await optimize(state, options.include, options.exclude);
77
75
  console.timeEnd('Done');
78
76
  }
79
77
  if (!options.onlyoptim && !options.fast) {
80
78
  printTitle('PASS 2 - Compressing the rest');
81
79
  console.time('Done');
82
- await compressFolder(options.exclude);
80
+ await compressFolder(state, options.exclude);
83
81
  console.timeEnd('Done');
84
82
  }
85
- printSummary();
86
- printIssues();
87
- if (options.fail && $state.issues.size > 0) {
83
+ printSummary(state);
84
+ printIssues(state);
85
+ if (options.fail && state.issues.size > 0) {
88
86
  exit(1);
89
87
  }
90
88
  });
91
89
  program.parse();
92
- function printSummary() {
90
+ function printSummary($state) {
93
91
  if ($state.summary.nbFiles > 0) {
94
92
  printTitle('Summary');
95
93
  const dataTable = [
@@ -141,22 +139,22 @@ function printSummary() {
141
139
  console.log(table(dataTable, config));
142
140
  }
143
141
  }
144
- function printIssues() {
142
+ function printIssues(state) {
145
143
  let issueCount = 0;
146
- if ($state.issues.size === 0) {
144
+ if (state.issues.size === 0) {
147
145
  printTitle('✔ No issues');
148
146
  }
149
147
  else {
150
148
  printTitle('Issues', kleur.bgRed);
151
149
  console.log('');
152
- for (let [file, list] of $state.issues) {
150
+ for (let [file, list] of state.issues) {
153
151
  console.log('▶ ' + file + '\n');
154
152
  list.forEach((issue) => {
155
153
  issueCount++;
156
154
  console.log(`${kleur.bgYellow(` ${issue.type} `)} ${issue.msg}\n`);
157
155
  });
158
156
  }
159
- printTitle(`${issueCount} issue(s) over ${$state.issues.size} files`, kleur.bgYellow);
157
+ printTitle(`${issueCount} issue(s) over ${state.issues.size} files`, kleur.bgYellow);
160
158
  console.log('');
161
159
  }
162
160
  }
@@ -1 +1,2 @@
1
- export declare function optimize(include?: string, exclude?: string): Promise<void>;
1
+ import { GlobalState } from './state.js';
2
+ export declare function optimize(state: GlobalState, include?: string, exclude?: string): Promise<void>;
package/dist/optimize.js CHANGED
@@ -4,10 +4,8 @@ import * as fs from 'fs/promises';
4
4
  import { getCanonicalCdnForUrl, getTransformer } from 'unpic';
5
5
  import * as cheerio from '@divriots/cheerio';
6
6
  import { isNumeric } from './utils.js';
7
- import config from './config.js';
8
7
  import { compressImage, } from './compressors/images.js';
9
8
  import svgToMiniDataURI from 'mini-svg-data-uri';
10
- import $state from './state.js';
11
9
  import kleur from 'kleur';
12
10
  import ora from 'ora';
13
11
  import { isLocal, Resource, translateSrc } from './utils/resource.js';
@@ -25,9 +23,9 @@ function getIntAttr(img, attr) {
25
23
  return;
26
24
  return parsed;
27
25
  }
28
- async function analyse(file) {
26
+ async function analyse(state, file) {
29
27
  console.log('▶ ' + file);
30
- const html = (await fs.readFile(path.join($state.dir, file))).toString();
28
+ const html = (await fs.readFile(path.join(state.dir, file))).toString();
31
29
  if (!html.includes('<html'))
32
30
  return;
33
31
  const $ = cheerio.load(html, { sourceCodeLocationInfo: true });
@@ -46,10 +44,10 @@ async function analyse(file) {
46
44
  const isAboveTheFold = isImgAboveTheFold(img, imgElement, theFold);
47
45
  img.removeAttr(ABOVE_FOLD_DATA_ATTR);
48
46
  try {
49
- await processImage(file, img, isAboveTheFold);
47
+ await processImage(state, file, img, isAboveTheFold);
50
48
  }
51
49
  catch (e) {
52
- $state.reportIssue(file, {
50
+ state.reportIssue(file, {
53
51
  type: 'erro',
54
52
  msg: e.message ||
55
53
  `Unexpected error while processing image: ${JSON.stringify(e)}`,
@@ -59,7 +57,7 @@ async function analyse(file) {
59
57
  // Reset spinner
60
58
  spinnerImg.text = kleur.dim(`<img> [${imgsArray.length}/${imgsArray.length}]`);
61
59
  // Notify issues
62
- const issues = $state.issues.get(file);
60
+ const issues = state.issues.get(file);
63
61
  if (issues) {
64
62
  spinnerImg.fail();
65
63
  console.log(kleur.red(` ${issues.length} issue${issues.length > 1 ? 's' : ''}`));
@@ -82,7 +80,7 @@ async function analyse(file) {
82
80
  await processIframe(file, $, ifElement, isAboveTheFold);
83
81
  }
84
82
  catch (e) {
85
- $state.reportIssue(file, {
83
+ state.reportIssue(file, {
86
84
  type: 'erro',
87
85
  msg: e.message ||
88
86
  `Unexpected error while processing image: ${JSON.stringify(e)}`,
@@ -104,7 +102,8 @@ async function analyse(file) {
104
102
  const heads = $('head'); // always exists because doc loaded with isDocument=true (default)
105
103
  const charsetElements$ = heads.find('meta[charset]');
106
104
  const wasCharsetFirst = !charsetElements$.prev().length;
107
- if (config.html.add_css_reset_as === 'inline') {
105
+ const { options } = state;
106
+ if (options.html.add_css_reset_as === 'inline') {
108
107
  prependToHead += `<style>:where(img){height:auto;}</style>`;
109
108
  }
110
109
  if (prependToHead || appendToHead) {
@@ -115,7 +114,7 @@ async function analyse(file) {
115
114
  }
116
115
  switch (charsetElements$.length) {
117
116
  case 0:
118
- $state.reportIssue(file, {
117
+ state.reportIssue(file, {
119
118
  type: 'fix',
120
119
  msg: 'Adding missing <meta charset="utf-8"> to the top of the <head>',
121
120
  });
@@ -123,7 +122,7 @@ async function analyse(file) {
123
122
  break;
124
123
  case 1:
125
124
  if (!wasCharsetFirst) {
126
- $state.reportIssue(file, {
125
+ state.reportIssue(file, {
127
126
  type: 'perf',
128
127
  msg: 'Moving <meta charset> to the top of the <head>',
129
128
  });
@@ -132,7 +131,7 @@ async function analyse(file) {
132
131
  heads.prepend(charsetElements$);
133
132
  break;
134
133
  default:
135
- $state.reportIssue(file, {
134
+ state.reportIssue(file, {
136
135
  type: 'fix',
137
136
  msg: 'Multiple <meta charset> found in the <head>: taking only the first one and deleting the others',
138
137
  });
@@ -142,8 +141,8 @@ async function analyse(file) {
142
141
  // Add to <body>
143
142
  //
144
143
  let appendToBody = '';
145
- if (config.misc.prefetch_links === 'in-viewport') {
146
- appendToBody += await prefetch_links_in_viewport(file);
144
+ if (options.misc.prefetch_links === 'in-viewport') {
145
+ appendToBody += await prefetch_links_in_viewport(state, file);
147
146
  }
148
147
  if (appendToBody) {
149
148
  const body = $('body');
@@ -156,16 +155,16 @@ async function analyse(file) {
156
155
  let htmlResult = $.html();
157
156
  // Inline critical css
158
157
  //
159
- if (config.css.inline_critical_css) {
158
+ if (options.css.inline_critical_css) {
160
159
  try {
161
- htmlResult = await inlineCriticalCss($state.dir, htmlResult);
160
+ htmlResult = await inlineCriticalCss(state.dir, htmlResult);
162
161
  }
163
162
  catch (e) {
164
163
  console.warn('Fail to inline critical CSS', e);
165
164
  }
166
165
  }
167
- if (!$state.args.nowrite) {
168
- await fs.writeFile(path.join($state.dir, file), htmlResult);
166
+ if (!state.args.nowrite) {
167
+ await fs.writeFile(path.join(state.dir, file), htmlResult);
169
168
  }
170
169
  }
171
170
  function isImgAboveTheFold(img, imgElement, theFold) {
@@ -200,7 +199,7 @@ function getTheFold($) {
200
199
  }
201
200
  return 0;
202
201
  }
203
- async function processImage(htmlfile, img, isAboveTheFold) {
202
+ async function processImage(state, htmlfile, img, isAboveTheFold) {
204
203
  var _a, _b;
205
204
  /*
206
205
  * Attribute 'src'
@@ -214,18 +213,23 @@ async function processImage(htmlfile, img, isAboveTheFold) {
214
213
  // Let's not mess with it and hope it's well done
215
214
  return; // No warnings
216
215
  }
217
- $state.reportIssue(htmlfile, {
216
+ state.reportIssue(htmlfile, {
218
217
  type: 'warn',
219
218
  msg: `Missing [src] on img - processing skipped.`,
220
219
  });
221
220
  return;
222
221
  }
222
+ const config = state.options;
223
+ if (!isIncluded(attrib_src, config.image.src_include, config.image.src_exclude)) {
224
+ // Setup to ignore
225
+ return;
226
+ }
223
227
  /*
224
228
  * Attribute 'alt'
225
229
  */
226
230
  const attrib_alt = img.attr('alt');
227
231
  if (attrib_alt === undefined) {
228
- $state.reportIssue(htmlfile, {
232
+ state.reportIssue(htmlfile, {
229
233
  type: 'a11y',
230
234
  msg: `Missing [alt] on img src="${attrib_src}" - Adding alt="" meanwhile.`,
231
235
  });
@@ -257,7 +261,7 @@ async function processImage(htmlfile, img, isAboveTheFold) {
257
261
  // Don't touch it
258
262
  break;
259
263
  default:
260
- $state.reportIssue(htmlfile, {
264
+ state.reportIssue(htmlfile, {
261
265
  type: 'invalid',
262
266
  msg: `Invalid [loading]="${attr_loading}" on img src="${attrib_src}"`,
263
267
  });
@@ -290,7 +294,7 @@ async function processImage(htmlfile, img, isAboveTheFold) {
290
294
  break;
291
295
  let attrib_width = getIntAttr(img, 'width');
292
296
  if (!attrib_width) {
293
- $state.reportIssue(htmlfile, {
297
+ state.reportIssue(htmlfile, {
294
298
  type: 'warn',
295
299
  msg: `Missing or malformed'width' attribute for image ${attrib_src}, unable to perform CDN transform`,
296
300
  });
@@ -298,13 +302,13 @@ async function processImage(htmlfile, img, isAboveTheFold) {
298
302
  }
299
303
  let attrib_height = getIntAttr(img, 'height');
300
304
  if (!attrib_height) {
301
- $state.reportIssue(htmlfile, {
305
+ state.reportIssue(htmlfile, {
302
306
  type: 'warn',
303
307
  msg: `Missing or malformed 'height' attribute for image ${attrib_src}, unable to perform CDN transform`,
304
308
  });
305
309
  return;
306
310
  }
307
- const new_srcset = await generateSrcSetForCdn(attrib_src, cdnTransformer, attrib_width, attrib_height);
311
+ const new_srcset = await generateSrcSetForCdn(state, attrib_src, cdnTransformer, attrib_width, attrib_height);
308
312
  if (new_srcset !== null) {
309
313
  img.attr('srcset', new_srcset);
310
314
  }
@@ -322,11 +326,11 @@ async function processImage(htmlfile, img, isAboveTheFold) {
322
326
  if (!isIncluded(attrib_src, config.image.external.src_include, config.image.external.src_exclude))
323
327
  break;
324
328
  try {
325
- attrib_src = await downloadExternalImage(htmlfile, attrib_src);
329
+ attrib_src = await downloadExternalImage(state, htmlfile, attrib_src);
326
330
  img.attr('src', attrib_src);
327
331
  }
328
332
  catch (e) {
329
- $state.reportIssue(htmlfile, {
333
+ state.reportIssue(htmlfile, {
330
334
  type: 'warn',
331
335
  msg: `Failed to download image: ${attrib_src} - ${e.message}`,
332
336
  });
@@ -338,10 +342,10 @@ async function processImage(htmlfile, img, isAboveTheFold) {
338
342
  /*
339
343
  * Loading image
340
344
  */
341
- const originalImage = await Resource.loadResource($state.dir, htmlfile, attrib_src);
345
+ const originalImage = await Resource.loadResource(state.dir, htmlfile, attrib_src);
342
346
  // No file -> give up
343
347
  if (!originalImage) {
344
- $state.reportIssue(htmlfile, {
348
+ state.reportIssue(htmlfile, {
345
349
  type: 'erro',
346
350
  msg: `Can't find img on disk src="${attrib_src}"`,
347
351
  });
@@ -365,7 +369,7 @@ async function processImage(htmlfile, img, isAboveTheFold) {
365
369
  : 'webp';
366
370
  }
367
371
  if (config.image.compress) {
368
- newImage = await compressImage(await originalImage.getData(), {
372
+ newImage = await compressImage(state, await originalImage.getData(), {
369
373
  toFormat: srcToFormat,
370
374
  resize: { width: config.image.srcset_max_width },
371
375
  });
@@ -374,13 +378,13 @@ async function processImage(htmlfile, img, isAboveTheFold) {
374
378
  const newFilename = path.join(path.dirname(originalImage.filePathAbsolute), path.basename(originalImage.filePathAbsolute, path.extname(originalImage.filePathAbsolute)) + `.${newImage.format}`);
375
379
  const newSrc = path.join(path.dirname(attrib_src), path.basename(attrib_src, path.extname(attrib_src)) +
376
380
  `.${newImage.format}`);
377
- if (!$state.compressedFiles.has(newFilename) && !$state.args.nowrite) {
381
+ if (!state.compressedFiles.has(newFilename) && !state.args.nowrite) {
378
382
  fs.writeFile(newFilename, newImage.data);
379
383
  }
380
384
  // Mark new file as compressed
381
- $state.compressedFiles.add(newFilename);
385
+ state.compressedFiles.add(newFilename);
382
386
  // Report compression result
383
- $state.reportSummary({
387
+ state.reportSummary({
384
388
  action: newFilename !== originalImage.filePathAbsolute
385
389
  ? `${await originalImage.getExt()}->${newImage.format}`
386
390
  : path.extname(originalImage.filePathAbsolute),
@@ -393,7 +397,7 @@ async function processImage(htmlfile, img, isAboveTheFold) {
393
397
  // Drop new Image
394
398
  newImage = undefined;
395
399
  // Report non-compression
396
- $state.reportSummary({
400
+ state.reportSummary({
397
401
  action: path.extname(originalImage.filePathAbsolute),
398
402
  originalSize: await originalImage.getLen(),
399
403
  compressedSize: await originalImage.getLen(),
@@ -433,7 +437,7 @@ async function processImage(htmlfile, img, isAboveTheFold) {
433
437
  img.attr('src', datauri);
434
438
  img.removeAttr('loading');
435
439
  img.removeAttr('decoding');
436
- $state.reportSummary({
440
+ state.reportSummary({
437
441
  action: `${imageToEmbed.format}->embed`,
438
442
  originalSize: await originalImage.getLen(),
439
443
  compressedSize: imageToEmbed.data.length,
@@ -450,7 +454,7 @@ async function processImage(htmlfile, img, isAboveTheFold) {
450
454
  /*
451
455
  * Attribute 'width' & 'height'
452
456
  */
453
- const [w, h] = await setImageSize(htmlfile, img, originalImage);
457
+ const [w, h] = await setImageSize(state, htmlfile, img, originalImage);
454
458
  if (isEmbed) {
455
459
  // Image is embed, no need for more processing
456
460
  return;
@@ -475,7 +479,7 @@ async function processImage(htmlfile, img, isAboveTheFold) {
475
479
  // of the srcset
476
480
  }
477
481
  else {
478
- const new_srcset = await generateSrcSet(htmlfile, originalImage, img.attr('src'), imageLengthToBeatInSrcSet, { toFormat: srcToFormat });
482
+ const new_srcset = await generateSrcSet(state, htmlfile, originalImage, img.attr('src'), imageLengthToBeatInSrcSet, { toFormat: srcToFormat });
479
483
  if (new_srcset !== null) {
480
484
  img.attr('srcset', new_srcset);
481
485
  }
@@ -529,7 +533,7 @@ async function processImage(htmlfile, img, isAboveTheFold) {
529
533
  // Ignore the creation of sources that already exist
530
534
  continue;
531
535
  }
532
- const srcset = await generateSrcSet(htmlfile, originalImage, undefined, undefined, {
536
+ const srcset = await generateSrcSet(state, htmlfile, originalImage, undefined, undefined, {
533
537
  toFormat: s.format,
534
538
  });
535
539
  if (!srcset) {
@@ -543,7 +547,7 @@ async function processImage(htmlfile, img, isAboveTheFold) {
543
547
  }
544
548
  }
545
549
  const isIncluded = (src, includeConf, excludeConf) => !!src.match(includeConf) && (!excludeConf || !src.match(excludeConf));
546
- async function _generateSrcSet(startSrc, imageWidth, imageHeight, transformSrc) {
550
+ async function _generateSrcSet({ options }, startSrc, imageWidth, imageHeight, transformSrc) {
547
551
  // Start from original image
548
552
  let new_srcset = '';
549
553
  if (!imageWidth || !imageHeight) {
@@ -553,8 +557,8 @@ async function _generateSrcSet(startSrc, imageWidth, imageHeight, transformSrc)
553
557
  // Start reduction
554
558
  const step = 300; //px
555
559
  let valueW = !startSrc ? imageWidth : imageWidth - step;
556
- valueW = Math.min(valueW, config.image.srcset_max_width);
557
- while (valueW >= config.image.srcset_min_width) {
560
+ valueW = Math.min(valueW, options.image.srcset_max_width);
561
+ while (valueW >= options.image.srcset_min_width) {
558
562
  let src = await transformSrc(valueW);
559
563
  if (src) {
560
564
  new_srcset += `, ${src} ${valueW}w`;
@@ -569,7 +573,7 @@ async function _generateSrcSet(startSrc, imageWidth, imageHeight, transformSrc)
569
573
  }
570
574
  return null;
571
575
  }
572
- async function generateSrcSet(htmlfile, originalImage, startSrc, startSrcLength, options) {
576
+ async function generateSrcSet(state, htmlfile, originalImage, startSrc, startSrcLength, options) {
573
577
  const ext = path.extname(originalImage.src);
574
578
  const fullbasename = originalImage.src.slice(0, -ext.length);
575
579
  const imageSrc = (addition) => {
@@ -582,12 +586,12 @@ async function generateSrcSet(htmlfile, originalImage, startSrc, startSrcLength,
582
586
  const imageWidth = (meta === null || meta === void 0 ? void 0 : meta.width) || 0;
583
587
  const imageHeight = (meta === null || meta === void 0 ? void 0 : meta.height) || 0;
584
588
  let previousImageSize = startSrcLength || Number.MAX_VALUE;
585
- return _generateSrcSet(startSrc, imageWidth, imageHeight, async (valueW) => {
589
+ return _generateSrcSet(state, startSrc, imageWidth, imageHeight, async (valueW) => {
586
590
  const src = imageSrc(`@${valueW}w`);
587
- const absoluteFilename = translateSrc($state.dir, path.dirname(htmlfile), src);
591
+ const absoluteFilename = translateSrc(state.dir, path.dirname(htmlfile), src);
588
592
  // Don't generate srcset file twice
589
- if (!$state.compressedFiles.has(absoluteFilename)) {
590
- const compressedImage = await compressImage(await originalImage.getData(), { ...options, resize: { width: valueW } });
593
+ if (!state.compressedFiles.has(absoluteFilename)) {
594
+ const compressedImage = await compressImage(state, await originalImage.getData(), { ...options, resize: { width: valueW } });
591
595
  if (!(compressedImage === null || compressedImage === void 0 ? void 0 : compressedImage.data) ||
592
596
  compressedImage.data.length >= previousImageSize) {
593
597
  // New image is not compressed or bigger than previous image in srcset
@@ -596,20 +600,20 @@ async function generateSrcSet(htmlfile, originalImage, startSrc, startSrcLength,
596
600
  }
597
601
  else {
598
602
  // Write file
599
- if (!$state.args.nowrite) {
603
+ if (!state.args.nowrite) {
600
604
  fs.writeFile(absoluteFilename, compressedImage.data);
601
605
  }
602
606
  // Set new previous size to beat
603
607
  previousImageSize = compressedImage.data.length;
604
608
  // Add file to list avoid recompression
605
- $state.compressedFiles.add(absoluteFilename);
609
+ state.compressedFiles.add(absoluteFilename);
606
610
  }
607
611
  }
608
612
  return src;
609
613
  });
610
614
  }
611
- async function generateSrcSetForCdn(startSrc, cdnTransformer, imageWidth, imageHeight) {
612
- return _generateSrcSet('', imageWidth, imageHeight, (valueW) => {
615
+ async function generateSrcSetForCdn(state, startSrc, cdnTransformer, imageWidth, imageHeight) {
616
+ return _generateSrcSet(state, '', imageWidth, imageHeight, (valueW) => {
613
617
  var _a, _b;
614
618
  return (_b = (_a = cdnTransformer({
615
619
  url: startSrc,
@@ -617,19 +621,19 @@ async function generateSrcSetForCdn(startSrc, cdnTransformer, imageWidth, imageH
617
621
  })) === null || _a === void 0 ? void 0 : _a.toString()) === null || _b === void 0 ? void 0 : _b.replace(UNPIC_DEFAULT_HOST_REGEX, '/');
618
622
  });
619
623
  }
620
- async function setImageSize(htmlfile, img, image) {
624
+ async function setImageSize(state, htmlfile, img, image) {
621
625
  let width = img.attr('width');
622
626
  let height = img.attr('height');
623
627
  let width_new = undefined;
624
628
  let height_new = undefined;
625
629
  const img_fmt = await image.getExt();
626
- if (img_fmt === 'svg' && !config.image.svg.add_width_and_height) {
630
+ if (img_fmt === 'svg' && !state.options.image.svg.add_width_and_height) {
627
631
  return [];
628
632
  }
629
633
  // Check valid values
630
634
  if (width !== undefined) {
631
635
  if (!isNumeric(width)) {
632
- $state.reportIssue(htmlfile, {
636
+ state.reportIssue(htmlfile, {
633
637
  type: 'fix',
634
638
  msg: `Invalid width attribute format: "${width}" - overriding for ${image.src}`,
635
639
  });
@@ -638,7 +642,7 @@ async function setImageSize(htmlfile, img, image) {
638
642
  }
639
643
  if (height !== undefined) {
640
644
  if (!isNumeric(height)) {
641
- $state.reportIssue(htmlfile, {
645
+ state.reportIssue(htmlfile, {
642
646
  type: 'fix',
643
647
  msg: `Invalid height attribute format: "${height}" - overriding for ${image.src}`,
644
648
  });
@@ -746,13 +750,13 @@ async function processIframe(htmlfile, $, imgElement, isAboveTheFold) {
746
750
  iframe.attr('loading', 'lazy');
747
751
  }
748
752
  }
749
- export async function optimize(include, exclude) {
753
+ export async function optimize(state, include, exclude) {
750
754
  const glob = include ? [include] : ['**/*.{htm,html}'];
751
755
  if (exclude)
752
756
  glob.push('!' + exclude);
753
- const paths = await globby(glob, { cwd: $state.dir });
757
+ const paths = await globby(glob, { cwd: state.dir });
754
758
  // Sequential async
755
759
  for (const file of paths) {
756
- await analyse(file);
760
+ await analyse(state, file);
757
761
  }
758
762
  }
@@ -1,2 +1,3 @@
1
+ import { GlobalState } from '../state.js';
1
2
  import '../utils/polyfill-fetch.js';
2
- export declare function downloadExternalImage(htmlfile: string, href: string): Promise<string>;
3
+ export declare function downloadExternalImage(state: GlobalState, htmlfile: string, href: string): Promise<string>;
@@ -1,12 +1,11 @@
1
1
  import * as path from 'path';
2
2
  import * as fs from 'fs/promises';
3
- import $state from '../state.js';
4
3
  import { hashSync as hasha } from 'hasha';
5
4
  import { fileTypeFromBuffer } from 'file-type';
6
5
  import { addToCache, getFromCache } from '../cache.js';
7
6
  import { parse } from '../utils/cache-control-parser.js';
8
7
  import '../utils/polyfill-fetch.js';
9
- export async function downloadExternalImage(htmlfile, href) {
8
+ export async function downloadExternalImage(state, htmlfile, href) {
10
9
  var _a, _b, _c, _d, _e;
11
10
  const hash = hasha(href, {
12
11
  algorithm: 'md5',
@@ -15,7 +14,7 @@ export async function downloadExternalImage(htmlfile, href) {
15
14
  let buffer;
16
15
  let ext;
17
16
  // Image is in cache?
18
- const dataFromCache = await getFromCache('img-ext', hash);
17
+ const dataFromCache = await getFromCache(state, 'img-ext', hash);
19
18
  const cacheControl = (_a = dataFromCache === null || dataFromCache === void 0 ? void 0 : dataFromCache.meta) === null || _a === void 0 ? void 0 : _a.CacheControl;
20
19
  if (dataFromCache && cacheControl) {
21
20
  const date = (_b = dataFromCache === null || dataFromCache === void 0 ? void 0 : dataFromCache.meta) === null || _b === void 0 ? void 0 : _b.Date;
@@ -64,7 +63,7 @@ export async function downloadExternalImage(htmlfile, href) {
64
63
  }
65
64
  else {
66
65
  // Add downloaded image to cache
67
- await addToCache('img-ext', hash, {
66
+ await addToCache(state, 'img-ext', hash, {
68
67
  buffer,
69
68
  meta: {
70
69
  Date: resp.headers.get('Date'),
@@ -97,7 +96,7 @@ export async function downloadExternalImage(htmlfile, href) {
97
96
  if (!ext)
98
97
  throw new Error('Unknown image format');
99
98
  const htmlFolder = path.dirname(htmlfile);
100
- const filename = path.relative(path.join($state.dir, htmlFolder), path.join($state.dir, `_jampack/${contentHash}.${ext}`));
101
- await fs.writeFile(path.join($state.dir, htmlFolder, filename), buffer);
99
+ const filename = path.relative(path.join(state.dir, htmlFolder), path.join(state.dir, `_jampack/${contentHash}.${ext}`));
100
+ await fs.writeFile(path.join(state.dir, htmlFolder, filename), buffer);
102
101
  return filename;
103
102
  }
@@ -1 +1,2 @@
1
- export declare function prefetch_links_in_viewport(html_file: string): Promise<string>;
1
+ import { GlobalState } from '../state.js';
2
+ export declare function prefetch_links_in_viewport(state: GlobalState, html_file: string): Promise<string>;
@@ -1,4 +1,3 @@
1
- import $state from '../state.js';
2
1
  import * as fs from 'fs/promises';
3
2
  import * as path from 'path';
4
3
  import { createRequire } from 'node:module';
@@ -6,13 +5,15 @@ const require = createRequire(import.meta.url);
6
5
  const src_filename = 'quicklink.mjs';
7
6
  const folder = `/_jampack/quicklink-2.3.0`;
8
7
  const url_loader = `${folder}/loader.js`;
9
- export async function prefetch_links_in_viewport(html_file) {
8
+ export async function prefetch_links_in_viewport(state, html_file) {
10
9
  const path_html = path.dirname('/' + html_file);
11
- const quickLinkDestination = path.join($state.dir, folder, src_filename);
12
- const payloadStored = await fs.access(quickLinkDestination).catch(() => false);
10
+ const quickLinkDestination = path.join(state.dir, folder, src_filename);
11
+ const payloadStored = await fs
12
+ .access(quickLinkDestination)
13
+ .catch(() => false);
13
14
  if (!payloadStored) {
14
- const path_loader = path.join($state.dir, url_loader);
15
- await fs.mkdir(path.join($state.dir, folder), { recursive: true });
15
+ const path_loader = path.join(state.dir, url_loader);
16
+ await fs.mkdir(path.join(state.dir, folder), { recursive: true });
16
17
  // Write loader
17
18
  const code_loader = `import { listen } from "./${src_filename}";
18
19
  listen();`;
package/dist/state.d.ts CHANGED
@@ -21,6 +21,8 @@ type Issue = {
21
21
  export declare class GlobalState {
22
22
  dir: string;
23
23
  args: Args;
24
+ options: import("./config-types.js").Options;
25
+ targets: import("lightningcss").Targets;
24
26
  compressedFiles: Set<string>;
25
27
  issues: Map<string, Issue[]>;
26
28
  summary: Summary;
@@ -28,5 +30,4 @@ export declare class GlobalState {
28
30
  reportIssue(sourceFile: string, issue: Issue): void;
29
31
  reportSummary(r: ReportItem): void;
30
32
  }
31
- declare const state: GlobalState;
32
- export default state;
33
+ export {};
package/dist/state.js CHANGED
@@ -1,7 +1,11 @@
1
+ import { defaultTargets } from './compressors/css.js';
2
+ import default_options from './config-default.js';
1
3
  export class GlobalState {
2
4
  constructor() {
3
5
  this.dir = 'dist';
4
6
  this.args = {};
7
+ this.options = default_options;
8
+ this.targets = defaultTargets();
5
9
  this.compressedFiles = new Set();
6
10
  this.issues = new Map();
7
11
  this.summary = {
@@ -23,9 +27,9 @@ export class GlobalState {
23
27
  reportSummary(r) {
24
28
  const isCompressed = r.compressedSize < r.originalSize ? 1 : 0;
25
29
  this.summary.nbFiles++;
26
- state.summary.nbFilesCompressed += isCompressed;
27
- state.summary.dataLenUncompressed += r.originalSize;
28
- state.summary.dataLenCompressed += r.compressedSize;
30
+ this.summary.nbFilesCompressed += isCompressed;
31
+ this.summary.dataLenUncompressed += r.originalSize;
32
+ this.summary.dataLenCompressed += r.compressedSize;
29
33
  if (r.action) {
30
34
  let summary = this.summaryByExtension[r.action];
31
35
  if (!summary) {
@@ -44,5 +48,3 @@ export class GlobalState {
44
48
  }
45
49
  }
46
50
  }
47
- const state = new GlobalState();
48
- export default state;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@divriots/jampack",
3
- "version": "0.24.3",
3
+ "version": "0.25.0",
4
4
  "cache-version": {
5
5
  "img": "v1",
6
6
  "img-ext": "v1"
@@ -26,6 +26,7 @@
26
26
  "./state": "./dist/state.js"
27
27
  },
28
28
  "dependencies": {
29
+ "@commander-js/extra-typings": "^12.0.0",
29
30
  "@divriots/cheerio": "1.0.0-rc.12",
30
31
  "@proload/core": "^0.3.3",
31
32
  "@swc/core": "^1.4.16",
@@ -40,7 +41,7 @@
40
41
  "hasha": "^6.0.0",
41
42
  "html-minifier-terser": "^7.2.0",
42
43
  "kleur": "^4.1.5",
43
- "lightningcss": "^1.24.1",
44
+ "lightningcss": "^1.25.1",
44
45
  "mini-svg-data-uri": "^1.4.4",
45
46
  "ora": "^8.0.1",
46
47
  "quicklink": "^2.3.0",