@divriots/jampack 0.24.4 → 0.26.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.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,
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,13 +53,13 @@ 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'));
@@ -68,28 +68,26 @@ program
68
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,12 +213,17 @@ 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 fullSizeRaw = img.attr('data-fullsize');
223
+ if (typeof fullSizeRaw !== undefined)
224
+ img.removeAttr('data-fullsize');
225
+ const fullSize = !!fullSizeRaw && parseInt(fullSizeRaw) > 0;
226
+ const config = state.options;
223
227
  if (!isIncluded(attrib_src, config.image.src_include, config.image.src_exclude)) {
224
228
  // Setup to ignore
225
229
  return;
@@ -229,7 +233,7 @@ async function processImage(htmlfile, img, isAboveTheFold) {
229
233
  */
230
234
  const attrib_alt = img.attr('alt');
231
235
  if (attrib_alt === undefined) {
232
- $state.reportIssue(htmlfile, {
236
+ state.reportIssue(htmlfile, {
233
237
  type: 'a11y',
234
238
  msg: `Missing [alt] on img src="${attrib_src}" - Adding alt="" meanwhile.`,
235
239
  });
@@ -261,7 +265,7 @@ async function processImage(htmlfile, img, isAboveTheFold) {
261
265
  // Don't touch it
262
266
  break;
263
267
  default:
264
- $state.reportIssue(htmlfile, {
268
+ state.reportIssue(htmlfile, {
265
269
  type: 'invalid',
266
270
  msg: `Invalid [loading]="${attr_loading}" on img src="${attrib_src}"`,
267
271
  });
@@ -292,9 +296,11 @@ async function processImage(htmlfile, img, isAboveTheFold) {
292
296
  break;
293
297
  if (!isIncluded(attrib_src, config.image.cdn.src_include || /.*/, config.image.cdn.src_exclude))
294
298
  break;
299
+ if (fullSize)
300
+ return;
295
301
  let attrib_width = getIntAttr(img, 'width');
296
302
  if (!attrib_width) {
297
- $state.reportIssue(htmlfile, {
303
+ state.reportIssue(htmlfile, {
298
304
  type: 'warn',
299
305
  msg: `Missing or malformed'width' attribute for image ${attrib_src}, unable to perform CDN transform`,
300
306
  });
@@ -302,13 +308,13 @@ async function processImage(htmlfile, img, isAboveTheFold) {
302
308
  }
303
309
  let attrib_height = getIntAttr(img, 'height');
304
310
  if (!attrib_height) {
305
- $state.reportIssue(htmlfile, {
311
+ state.reportIssue(htmlfile, {
306
312
  type: 'warn',
307
313
  msg: `Missing or malformed 'height' attribute for image ${attrib_src}, unable to perform CDN transform`,
308
314
  });
309
315
  return;
310
316
  }
311
- const new_srcset = await generateSrcSetForCdn(attrib_src, cdnTransformer, attrib_width, attrib_height);
317
+ const new_srcset = await generateSrcSetForCdn(state, attrib_src, cdnTransformer, attrib_width, attrib_height);
312
318
  if (new_srcset !== null) {
313
319
  img.attr('srcset', new_srcset);
314
320
  }
@@ -326,11 +332,11 @@ async function processImage(htmlfile, img, isAboveTheFold) {
326
332
  if (!isIncluded(attrib_src, config.image.external.src_include, config.image.external.src_exclude))
327
333
  break;
328
334
  try {
329
- attrib_src = await downloadExternalImage(htmlfile, attrib_src);
335
+ attrib_src = await downloadExternalImage(state, htmlfile, attrib_src);
330
336
  img.attr('src', attrib_src);
331
337
  }
332
338
  catch (e) {
333
- $state.reportIssue(htmlfile, {
339
+ state.reportIssue(htmlfile, {
334
340
  type: 'warn',
335
341
  msg: `Failed to download image: ${attrib_src} - ${e.message}`,
336
342
  });
@@ -342,10 +348,10 @@ async function processImage(htmlfile, img, isAboveTheFold) {
342
348
  /*
343
349
  * Loading image
344
350
  */
345
- const originalImage = await Resource.loadResource($state.dir, htmlfile, attrib_src);
351
+ const originalImage = await Resource.loadResource(state.dir, htmlfile, attrib_src);
346
352
  // No file -> give up
347
353
  if (!originalImage) {
348
- $state.reportIssue(htmlfile, {
354
+ state.reportIssue(htmlfile, {
349
355
  type: 'erro',
350
356
  msg: `Can't find img on disk src="${attrib_src}"`,
351
357
  });
@@ -369,7 +375,7 @@ async function processImage(htmlfile, img, isAboveTheFold) {
369
375
  : 'webp';
370
376
  }
371
377
  if (config.image.compress) {
372
- newImage = await compressImage(await originalImage.getData(), {
378
+ newImage = await compressImage(state, await originalImage.getData(), {
373
379
  toFormat: srcToFormat,
374
380
  resize: { width: config.image.srcset_max_width },
375
381
  });
@@ -378,13 +384,13 @@ async function processImage(htmlfile, img, isAboveTheFold) {
378
384
  const newFilename = path.join(path.dirname(originalImage.filePathAbsolute), path.basename(originalImage.filePathAbsolute, path.extname(originalImage.filePathAbsolute)) + `.${newImage.format}`);
379
385
  const newSrc = path.join(path.dirname(attrib_src), path.basename(attrib_src, path.extname(attrib_src)) +
380
386
  `.${newImage.format}`);
381
- if (!$state.compressedFiles.has(newFilename) && !$state.args.nowrite) {
387
+ if (!state.compressedFiles.has(newFilename) && !state.args.nowrite) {
382
388
  fs.writeFile(newFilename, newImage.data);
383
389
  }
384
390
  // Mark new file as compressed
385
- $state.compressedFiles.add(newFilename);
391
+ state.compressedFiles.add(newFilename);
386
392
  // Report compression result
387
- $state.reportSummary({
393
+ state.reportSummary({
388
394
  action: newFilename !== originalImage.filePathAbsolute
389
395
  ? `${await originalImage.getExt()}->${newImage.format}`
390
396
  : path.extname(originalImage.filePathAbsolute),
@@ -397,7 +403,7 @@ async function processImage(htmlfile, img, isAboveTheFold) {
397
403
  // Drop new Image
398
404
  newImage = undefined;
399
405
  // Report non-compression
400
- $state.reportSummary({
406
+ state.reportSummary({
401
407
  action: path.extname(originalImage.filePathAbsolute),
402
408
  originalSize: await originalImage.getLen(),
403
409
  compressedSize: await originalImage.getLen(),
@@ -437,7 +443,7 @@ async function processImage(htmlfile, img, isAboveTheFold) {
437
443
  img.attr('src', datauri);
438
444
  img.removeAttr('loading');
439
445
  img.removeAttr('decoding');
440
- $state.reportSummary({
446
+ state.reportSummary({
441
447
  action: `${imageToEmbed.format}->embed`,
442
448
  originalSize: await originalImage.getLen(),
443
449
  compressedSize: imageToEmbed.data.length,
@@ -454,7 +460,7 @@ async function processImage(htmlfile, img, isAboveTheFold) {
454
460
  /*
455
461
  * Attribute 'width' & 'height'
456
462
  */
457
- const [w, h] = await setImageSize(htmlfile, img, originalImage);
463
+ const [w, h] = await setImageSize(state, htmlfile, img, originalImage);
458
464
  if (isEmbed) {
459
465
  // Image is embed, no need for more processing
460
466
  return;
@@ -478,8 +484,8 @@ async function processImage(htmlfile, img, isAboveTheFold) {
478
484
  // The compress pass will compress the images
479
485
  // of the srcset
480
486
  }
481
- else {
482
- const new_srcset = await generateSrcSet(htmlfile, originalImage, img.attr('src'), imageLengthToBeatInSrcSet, { toFormat: srcToFormat });
487
+ else if (!fullSize) {
488
+ const new_srcset = await generateSrcSet(state, htmlfile, originalImage, img.attr('src'), imageLengthToBeatInSrcSet, { toFormat: srcToFormat });
483
489
  if (new_srcset !== null) {
484
490
  img.attr('srcset', new_srcset);
485
491
  }
@@ -533,7 +539,7 @@ async function processImage(htmlfile, img, isAboveTheFold) {
533
539
  // Ignore the creation of sources that already exist
534
540
  continue;
535
541
  }
536
- const srcset = await generateSrcSet(htmlfile, originalImage, undefined, undefined, {
542
+ const srcset = await generateSrcSet(state, htmlfile, originalImage, undefined, undefined, {
537
543
  toFormat: s.format,
538
544
  });
539
545
  if (!srcset) {
@@ -547,7 +553,7 @@ async function processImage(htmlfile, img, isAboveTheFold) {
547
553
  }
548
554
  }
549
555
  const isIncluded = (src, includeConf, excludeConf) => !!src.match(includeConf) && (!excludeConf || !src.match(excludeConf));
550
- async function _generateSrcSet(startSrc, imageWidth, imageHeight, transformSrc) {
556
+ async function _generateSrcSet({ options }, startSrc, imageWidth, imageHeight, transformSrc) {
551
557
  // Start from original image
552
558
  let new_srcset = '';
553
559
  if (!imageWidth || !imageHeight) {
@@ -557,8 +563,8 @@ async function _generateSrcSet(startSrc, imageWidth, imageHeight, transformSrc)
557
563
  // Start reduction
558
564
  const step = 300; //px
559
565
  let valueW = !startSrc ? imageWidth : imageWidth - step;
560
- valueW = Math.min(valueW, config.image.srcset_max_width);
561
- while (valueW >= config.image.srcset_min_width) {
566
+ valueW = Math.min(valueW, options.image.srcset_max_width);
567
+ while (valueW >= options.image.srcset_min_width) {
562
568
  let src = await transformSrc(valueW);
563
569
  if (src) {
564
570
  new_srcset += `, ${src} ${valueW}w`;
@@ -573,7 +579,7 @@ async function _generateSrcSet(startSrc, imageWidth, imageHeight, transformSrc)
573
579
  }
574
580
  return null;
575
581
  }
576
- async function generateSrcSet(htmlfile, originalImage, startSrc, startSrcLength, options) {
582
+ async function generateSrcSet(state, htmlfile, originalImage, startSrc, startSrcLength, options) {
577
583
  const ext = path.extname(originalImage.src);
578
584
  const fullbasename = originalImage.src.slice(0, -ext.length);
579
585
  const imageSrc = (addition) => {
@@ -586,12 +592,12 @@ async function generateSrcSet(htmlfile, originalImage, startSrc, startSrcLength,
586
592
  const imageWidth = (meta === null || meta === void 0 ? void 0 : meta.width) || 0;
587
593
  const imageHeight = (meta === null || meta === void 0 ? void 0 : meta.height) || 0;
588
594
  let previousImageSize = startSrcLength || Number.MAX_VALUE;
589
- return _generateSrcSet(startSrc, imageWidth, imageHeight, async (valueW) => {
595
+ return _generateSrcSet(state, startSrc, imageWidth, imageHeight, async (valueW) => {
590
596
  const src = imageSrc(`@${valueW}w`);
591
- const absoluteFilename = translateSrc($state.dir, path.dirname(htmlfile), src);
597
+ const absoluteFilename = translateSrc(state.dir, path.dirname(htmlfile), src);
592
598
  // Don't generate srcset file twice
593
- if (!$state.compressedFiles.has(absoluteFilename)) {
594
- const compressedImage = await compressImage(await originalImage.getData(), { ...options, resize: { width: valueW } });
599
+ if (!state.compressedFiles.has(absoluteFilename)) {
600
+ const compressedImage = await compressImage(state, await originalImage.getData(), { ...options, resize: { width: valueW } });
595
601
  if (!(compressedImage === null || compressedImage === void 0 ? void 0 : compressedImage.data) ||
596
602
  compressedImage.data.length >= previousImageSize) {
597
603
  // New image is not compressed or bigger than previous image in srcset
@@ -600,20 +606,20 @@ async function generateSrcSet(htmlfile, originalImage, startSrc, startSrcLength,
600
606
  }
601
607
  else {
602
608
  // Write file
603
- if (!$state.args.nowrite) {
609
+ if (!state.args.nowrite) {
604
610
  fs.writeFile(absoluteFilename, compressedImage.data);
605
611
  }
606
612
  // Set new previous size to beat
607
613
  previousImageSize = compressedImage.data.length;
608
614
  // Add file to list avoid recompression
609
- $state.compressedFiles.add(absoluteFilename);
615
+ state.compressedFiles.add(absoluteFilename);
610
616
  }
611
617
  }
612
618
  return src;
613
619
  });
614
620
  }
615
- async function generateSrcSetForCdn(startSrc, cdnTransformer, imageWidth, imageHeight) {
616
- return _generateSrcSet('', imageWidth, imageHeight, (valueW) => {
621
+ async function generateSrcSetForCdn(state, startSrc, cdnTransformer, imageWidth, imageHeight) {
622
+ return _generateSrcSet(state, '', imageWidth, imageHeight, (valueW) => {
617
623
  var _a, _b;
618
624
  return (_b = (_a = cdnTransformer({
619
625
  url: startSrc,
@@ -621,19 +627,19 @@ async function generateSrcSetForCdn(startSrc, cdnTransformer, imageWidth, imageH
621
627
  })) === null || _a === void 0 ? void 0 : _a.toString()) === null || _b === void 0 ? void 0 : _b.replace(UNPIC_DEFAULT_HOST_REGEX, '/');
622
628
  });
623
629
  }
624
- async function setImageSize(htmlfile, img, image) {
630
+ async function setImageSize(state, htmlfile, img, image) {
625
631
  let width = img.attr('width');
626
632
  let height = img.attr('height');
627
633
  let width_new = undefined;
628
634
  let height_new = undefined;
629
635
  const img_fmt = await image.getExt();
630
- if (img_fmt === 'svg' && !config.image.svg.add_width_and_height) {
636
+ if (img_fmt === 'svg' && !state.options.image.svg.add_width_and_height) {
631
637
  return [];
632
638
  }
633
639
  // Check valid values
634
640
  if (width !== undefined) {
635
641
  if (!isNumeric(width)) {
636
- $state.reportIssue(htmlfile, {
642
+ state.reportIssue(htmlfile, {
637
643
  type: 'fix',
638
644
  msg: `Invalid width attribute format: "${width}" - overriding for ${image.src}`,
639
645
  });
@@ -642,7 +648,7 @@ async function setImageSize(htmlfile, img, image) {
642
648
  }
643
649
  if (height !== undefined) {
644
650
  if (!isNumeric(height)) {
645
- $state.reportIssue(htmlfile, {
651
+ state.reportIssue(htmlfile, {
646
652
  type: 'fix',
647
653
  msg: `Invalid height attribute format: "${height}" - overriding for ${image.src}`,
648
654
  });
@@ -750,13 +756,13 @@ async function processIframe(htmlfile, $, imgElement, isAboveTheFold) {
750
756
  iframe.attr('loading', 'lazy');
751
757
  }
752
758
  }
753
- export async function optimize(include, exclude) {
759
+ export async function optimize(state, include, exclude) {
754
760
  const glob = include ? [include] : ['**/*.{htm,html}'];
755
761
  if (exclude)
756
762
  glob.push('!' + exclude);
757
- const paths = await globby(glob, { cwd: $state.dir });
763
+ const paths = await globby(glob, { cwd: state.dir });
758
764
  // Sequential async
759
765
  for (const file of paths) {
760
- await analyse(file);
766
+ await analyse(state, file);
761
767
  }
762
768
  }
@@ -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.4",
3
+ "version": "0.26.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",
@@ -58,18 +59,17 @@
58
59
  "tslib": "^2.6.2",
59
60
  "typescript": "^5.4.5"
60
61
  },
62
+ "engines": {
63
+ "node": ">=14.0.0"
64
+ },
65
+ "bugs": "https://github.com/divriots/jampack/issues",
66
+ "keywords": [],
67
+ "types": "dist/index.d.ts",
61
68
  "scripts": {
62
69
  "watch": "pnpm build --watch",
63
70
  "try": "shx rm -fr demo && cp -R demo_drc demo && node ./dist/index.js ./demo --nocache",
64
71
  "build": "shx rm -rf dist && tsc -b",
65
72
  "lint": "eslint . --ext .ts --config .eslintrc",
66
- "prepublishOnly": "pnpm build",
67
73
  "cleanAllDotJampack": "find -s . | grep '.jampack$' | xargs rm -fr"
68
- },
69
- "engines": {
70
- "node": ">=14.0.0"
71
- },
72
- "bugs": "https://github.com/divriots/jampack/issues",
73
- "keywords": [],
74
- "types": "dist/index.d.ts"
74
+ }
75
75
  }