@divriots/jampack 0.1.8 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,74 @@
1
- ## jampack
1
+ ```
2
+ __ __
3
+ |__|____ _____ ___________ ____ | | __
4
+ | \__ \ / \\____ \__ \ _/ ___\| |/ /
5
+ | |/ __ \| Y Y \ |_> > __ \\ \___| <
6
+ /\__| (____ /__|_| / __(____ /\___ >__|_ \
7
+ \______| \/ \/| | \/ \/ \/
8
+ |__|
9
+ ```
10
+ [![npm version](https://img.shields.io/npm/v/@divriots/jampack)](https://npmjs.org/package/@divriots/jampack)
11
+ [![Discord](https://img.shields.io/badge/chat-discord-blue?style=flat&logo=discord)](https://discord.gg/XkQxSU9)
2
12
 
3
- Optimize static websites for maximum performance and best Core Web Vitals scores.
13
+ # `jampack`
14
+
15
+ Optimizes static websites for best user experience and best Core Web Vitals scores.
16
+
17
+ <div>
18
+ <div>Brought to you by</div>
19
+ <a href="https://divRIOTS.com#gh-light-mode-only">
20
+ <img width="150" height="40" src="https://divRIOTS.com/divriots.svg" alt="‹div›RIOTS" />
21
+ </a>
22
+ <a href="https://divRIOTS.com#gh-dark-mode-only">
23
+ <img width="150" height="40" src="https://divRIOTS.com/divriots-dark.svg" alt="‹div›RIOTS" />
24
+ </a>
25
+ </div>
26
+
27
+ ## What is `jampack`?
28
+
29
+ > Not a bundler. Not a framework.
30
+
31
+ `jampack` is a post-processing tool that takes the output of your [Static Site Generator (aka SSG)](https://jamstack.org/generators/) and optimizes it for best user experience and best [Core Web Vitals](https://web.dev/learn-core-web-vitals/) scores.
32
+
33
+ ## Why `jampack`?
34
+
35
+ - **Better user experience**. Specially on low-end devices.
36
+ - **Better SEO**. Page experience now [affects ranking](https://developers.google.com/search/docs/appearance/page-experience).
37
+ - **Easier maintenance**. Generating top performance webpages requires extra engineering in the composition of the pages that is counterproductive with the maintenance of the website.
38
+ - **Time saving**. Spend less time optimizing and let `jampack` perform the heaving-lifting for you.
39
+
40
+ ## Optimizations
41
+
42
+ Growing list of optimizations performed:
43
+
44
+ | Optimization | Description |
45
+ | ------------- | ------------- |
46
+ | todo | todo |
47
+
48
+ ## Quick use
49
+
50
+ ```sh
51
+ # Optimize static website in `dist` folder
52
+ npx @divriots/jampack ./dist
53
+ ```
54
+
55
+ ## Configuration
56
+
57
+ No configuration yet. All defaults.
58
+
59
+ ## `jampack` use in the wild
60
+
61
+ - [divRIOTS.com](https://divRIOTS.com)
62
+ - [Backlight.dev](https://backlight.dev)
63
+ - [story.to.design](https://story.to.design)
64
+ - [Components.studio](https://components.studio)
65
+ > Add yours here
66
+
67
+ ## Why is it called `jampack`?
68
+
69
+ - ` jam`: From [Jamstack](https://en.wikipedia.org/wiki/Jamstack).
70
+ - `pack`: Reminescent of the [Executable Packers](https://en.wikipedia.org/wiki/Executable_compression#List_of_executable_packers) from the 90s.
71
+
72
+ ## License
73
+
74
+ This software is released under the terms of the [MIT license](https://github.com/divriots/jampack/blob/main/LICENSE).
@@ -1,3 +1,4 @@
1
1
  import sharp from 'sharp';
2
- export declare const compressImage: (data: Buffer, resize: sharp.ResizeOptions) => Promise<Buffer | undefined>;
3
- export declare function compress(glob: string): Promise<void>;
2
+ import type { Image } from './types.js';
3
+ export declare const compressImage: (data: Buffer, resize: sharp.ResizeOptions, toWebp?: boolean) => Promise<Image | undefined>;
4
+ export declare function compress(exclude: string): Promise<void>;
package/dist/compress.js CHANGED
@@ -8,6 +8,7 @@ import sharp from 'sharp';
8
8
  import swc from '@swc/core';
9
9
  import globalState from './state.js';
10
10
  import { globby } from 'globby';
11
+ import options from './options.js';
11
12
  const beginProgress = () => {
12
13
  };
13
14
  const printProgress = () => {
@@ -27,24 +28,18 @@ const endProgress = () => {
27
28
  process.stdout.write('\n');
28
29
  };
29
30
  const processFile = async (file, stats) => {
30
- const result = {
31
- file,
32
- originalSize: stats.size,
33
- compressedSize: stats.size
34
- };
35
31
  let writeData = undefined;
36
32
  try {
37
33
  const ext = path.extname(file);
38
34
  switch (ext) {
39
- case '.svg':
40
35
  case '.png':
41
36
  case '.jpg':
42
37
  case '.jpeg':
38
+ case '.svg':
43
39
  case '.webp':
44
- //case '.gif':
45
- const newImageData = await compressImageFile(file);
46
- if (newImageData && newImageData.length < result.originalSize) {
47
- writeData = newImageData;
40
+ const newImage = await compressImageFile(file);
41
+ if ((newImage === null || newImage === void 0 ? void 0 : newImage.data) && newImage.data.length < stats.size) {
42
+ writeData = newImage.data;
48
43
  }
49
44
  break;
50
45
  case '.html':
@@ -74,6 +69,11 @@ const processFile = async (file, stats) => {
74
69
  console.error(`\n${file}`);
75
70
  console.error(e);
76
71
  }
72
+ const result = {
73
+ action: path.extname(file),
74
+ originalSize: stats.size,
75
+ compressedSize: stats.size
76
+ };
77
77
  // Writedata
78
78
  if (writeData && writeData.length < result.originalSize) {
79
79
  result.compressedSize = writeData.length;
@@ -81,12 +81,26 @@ const processFile = async (file, stats) => {
81
81
  await fs.writeFile(file, writeData);
82
82
  }
83
83
  }
84
- globalState.addFile(result);
84
+ globalState.compressedFiles.add(file);
85
+ globalState.reportItem(result);
85
86
  printProgress();
86
87
  };
87
- export const compressImage = async (data, resize) => {
88
- const sharpFile = await sharp(data, { animated: true });
88
+ function createWebpOptions(opt) {
89
+ return {
90
+ nearLossless: (opt === null || opt === void 0 ? void 0 : opt.mode) === 'lossless',
91
+ quality: (opt === null || opt === void 0 ? void 0 : opt.quality) || 80,
92
+ effort: (opt === null || opt === void 0 ? void 0 : opt.effort) || 4
93
+ };
94
+ }
95
+ export const compressImage = async (data, resize, toWebp = false) => {
96
+ let sharpFile = await sharp(data, { animated: true });
89
97
  const meta = await sharpFile.metadata();
98
+ if (meta.pages && meta.pages > 1) {
99
+ // Skip animated images for the moment.
100
+ return undefined;
101
+ }
102
+ let doSharp = false;
103
+ let outputFormat;
90
104
  switch (meta.format) {
91
105
  case 'svg':
92
106
  const newData = svgo(data, {
@@ -105,27 +119,51 @@ export const compressImage = async (data, resize) => {
105
119
  console.log(`Error processing svg ${data}`);
106
120
  return undefined;
107
121
  }
108
- return Buffer.from(newData.data, 'utf8');
122
+ return { format: 'svg', data: Buffer.from(newData.data, 'utf8') };
109
123
  case 'png':
110
- case 'jpg':
124
+ outputFormat = 'png';
125
+ if (!toWebp) {
126
+ sharpFile = sharpFile.png(options.image.png.options || {});
127
+ }
128
+ doSharp = true;
129
+ break;
111
130
  case 'jpeg':
112
- case 'webp':
113
- //case 'gif':
114
- let sf = sharpFile;
115
- if (resize.width && resize.height) {
116
- sf = sf.resize({ ...resize, fit: 'fill', withoutEnlargement: true });
131
+ case 'jpg':
132
+ outputFormat = 'jpg';
133
+ if (!toWebp) {
134
+ sharpFile = sharpFile.jpeg(options.image.jpeg.options || {});
117
135
  }
118
- return await sf.toBuffer();
136
+ doSharp = true;
137
+ break;
138
+ case 'webp':
139
+ outputFormat = 'webp';
140
+ sharpFile = sharpFile.webp(createWebpOptions(options.image.webp.options_lossly) || {});
141
+ doSharp = true;
142
+ toWebp = false; // Don't need to convert to webP again
143
+ break;
144
+ }
145
+ if (doSharp) {
146
+ if (toWebp) {
147
+ sharpFile = sharpFile.webp(createWebpOptions(meta.format === 'png' ? options.image.webp.options_lossless : options.image.webp.options_lossly));
148
+ outputFormat = 'webp';
149
+ }
150
+ if (resize.width && resize.height) {
151
+ sharpFile = sharpFile.resize({ ...resize, fit: 'fill', withoutEnlargement: true });
152
+ }
153
+ return { format: outputFormat, data: await sharpFile.toBuffer() };
119
154
  }
120
155
  return undefined;
121
156
  };
122
- const compressImageFile = async (file) => {
157
+ const compressImageFile = async (file, toWebP = false) => {
123
158
  const buffer = await fs.readFile(file);
124
- return compressImage(buffer, {});
159
+ return compressImage(buffer, {}, toWebP);
125
160
  };
126
- export async function compress(glob) {
161
+ export async function compress(exclude) {
127
162
  beginProgress();
128
- const paths = await globby(glob, { cwd: globalState.dir, absolute: true });
163
+ const globs = ['**/**'];
164
+ if (exclude)
165
+ globs.push('!' + exclude);
166
+ const paths = await globby(globs, { cwd: globalState.dir, absolute: true });
129
167
  // "Parallel" processing
130
168
  await Promise.all(paths.map(async (file) => {
131
169
  if (!globalState.compressedFiles.has(file)) {
package/dist/index.js CHANGED
@@ -23,34 +23,34 @@ program
23
23
  program.command('pack', { isDefault: true })
24
24
  .description('todo')
25
25
  .argument('<dir>', 'Directory to pack')
26
+ .option('--exclude <exclude>', 'Glob to exclude')
26
27
  .option('--nowrite', 'No write')
27
28
  .option('--onlycomp', 'Only compress')
28
29
  .option('--onlyoptim', 'Only optimize')
29
30
  .action(async (dir, options) => {
30
31
  globalState.dir = dir;
31
32
  globalState.args = options;
32
- if (!options.onlyoptim && !options.onlycomp) {
33
- console.log(kleur.bgGreen(kleur.black(`\n PASS 1 - Compressing embeddables `)));
34
- await compress("**/*.{css,svg,js}");
35
- }
36
33
  if (!options.onlycomp) {
37
- console.log(kleur.bgGreen(kleur.black(`\n PASS 2 - Optimizing `)));
38
- await optimize();
34
+ console.log('');
35
+ console.log(kleur.bgGreen(kleur.black(` PASS 1 - Optimizing `)));
36
+ await optimize(options.exclude);
39
37
  }
40
38
  if (!options.onlyoptim) {
41
- console.log(kleur.bgGreen(kleur.black(`\n PASS 3 - Compressing the rest`)));
42
- await compress("**/**");
39
+ console.log('');
40
+ console.log(kleur.bgGreen(kleur.black(` PASS 2 - Compressing the rest`)));
41
+ await compress(options.exclude);
43
42
  }
44
43
  printDetails();
45
44
  });
46
45
  program.parse();
47
46
  function printDetails() {
48
- const dataTable = [['Extension', 'Compressed', 'Original', 'Compressed', 'Gain']];
47
+ const dataTable = [['Action', 'Compressed', 'Original', 'Compressed', 'Gain']];
49
48
  const config = {
50
49
  columns: [
51
50
  { alignment: 'left' },
52
51
  { alignment: 'right' },
53
52
  { alignment: 'right' },
53
+ { alignment: 'right' },
54
54
  { alignment: 'right' }
55
55
  ],
56
56
  };
@@ -1 +1 @@
1
- export declare function optimize(): Promise<void>;
1
+ export declare function optimize(exclude?: string): Promise<void>;
package/dist/optimize.js CHANGED
@@ -83,7 +83,7 @@ async function processImage(file, $, imgElement) {
83
83
  const absoluteImgPath = translateSrc(globalState.dir, path.dirname(file), attrib_src);
84
84
  // file exists?
85
85
  try {
86
- const stats = await fs.stat(absoluteImgPath);
86
+ await fs.stat(absoluteImgPath);
87
87
  }
88
88
  catch (e) {
89
89
  console.error(`Missing img ${absoluteImgPath}`);
@@ -93,23 +93,51 @@ async function processImage(file, $, imgElement) {
93
93
  const meta = await sharpFile.metadata();
94
94
  const originalData = await fs.readFile(absoluteImgPath);
95
95
  /*
96
- * Embed small images
97
- */
96
+ * Compress image to WebP
97
+ */
98
+ let newImage;
99
+ if (!globalState.optimizedFiles.has(absoluteImgPath)) {
100
+ // Let's avoid to optimize same images twice
101
+ globalState.optimizedFiles.add(absoluteImgPath);
102
+ newImage = await compressImage(originalData, {}, true);
103
+ if ((newImage === null || newImage === void 0 ? void 0 : newImage.data) && newImage.data.length < originalData.length) {
104
+ // Do we need to add an new extension?
105
+ const newExtension = `.${newImage.format}`;
106
+ const additionalExtension = path.extname(absoluteImgPath) === newExtension ? '' : newExtension;
107
+ const newFilename = absoluteImgPath + additionalExtension;
108
+ if (!globalState.args.nowrite) {
109
+ fs.writeFile(newFilename, newImage.data);
110
+ }
111
+ globalState.compressedFiles.add(newFilename);
112
+ // Report compression result
113
+ globalState.reportItem({
114
+ action: (newFilename !== absoluteImgPath) ? `${meta.format}->${newImage.format}` : path.extname(absoluteImgPath),
115
+ originalSize: originalData.length,
116
+ compressedSize: newImage.data.length
117
+ });
118
+ img.attr('src', attrib_src + additionalExtension);
119
+ }
120
+ }
121
+ /*
122
+ * Embed small images
123
+ *
124
+ * TODO this is only embedding images that have
125
+ * successfully be compressed. Should embed original
126
+ * image if it fits the size.
127
+ */
98
128
  let isEmbed = false;
99
- if (originalData.length <= options.image.embed_size) {
129
+ if (newImage && newImage.data.length <= options.image.embed_size) {
100
130
  let datauri = undefined;
101
- switch (meta.format) {
131
+ switch (newImage.format) {
102
132
  case 'svg':
103
- const compressedData = await compressImage(originalData, {});
104
- const svgEmbed = (compressedData && compressedData.length < originalData.length) ? compressedData : originalData;
105
- datauri = svgToMiniDataURI(svgEmbed.toString());
133
+ datauri = svgToMiniDataURI(newImage.data.toString());
134
+ break;
135
+ case 'webp':
136
+ datauri = `data:image/webp;base64,${newImage.data.toString('base64')}`;
106
137
  break;
107
138
  case 'jpg':
108
- case 'jpeg':
109
139
  case 'png':
110
- case 'webp':
111
- //case 'gif':
112
- // TODO
140
+ // TODO but not possible in current code
113
141
  break;
114
142
  }
115
143
  if (datauri) {
@@ -117,31 +145,13 @@ async function processImage(file, $, imgElement) {
117
145
  img.attr('src', datauri);
118
146
  img.removeAttr('loading');
119
147
  img.removeAttr('decoding');
148
+ globalState.reportItem({ action: `${newImage.format}->embed`, originalSize: originalData.length, compressedSize: newImage.data.length });
120
149
  }
121
150
  }
122
151
  if (isEmbed) {
123
152
  // Image is embed, no need for more processing
124
153
  return;
125
154
  }
126
- /*
127
- * Compress image
128
- */
129
- if (!globalState.compressedFiles.has(absoluteImgPath)) {
130
- // Image not already compressed before
131
- const result = {
132
- file: absoluteImgPath,
133
- originalSize: originalData.length,
134
- compressedSize: originalData.length
135
- };
136
- const newImage = await compressImage(originalData, {});
137
- if (newImage && newImage.length < originalData.length) {
138
- if (!globalState.args.nowrite) {
139
- fs.writeFile(absoluteImgPath, newImage);
140
- }
141
- result.compressedSize = newImage.length;
142
- }
143
- globalState.addFile(result);
144
- }
145
155
  //
146
156
  // Stop here if svg
147
157
  //
@@ -168,7 +178,7 @@ async function processImage(file, $, imgElement) {
168
178
  // Generate image set
169
179
  const ext = path.extname(attrib_src);
170
180
  const fullbasename = attrib_src.slice(0, -(ext.length));
171
- const imageSrc = (addition) => `${fullbasename}${addition}${ext}`;
181
+ const imageSrc = (addition) => `${fullbasename}${addition}.webp`;
172
182
  // Start from original image
173
183
  let new_srcset = '';
174
184
  // Start reduction
@@ -178,20 +188,24 @@ async function processImage(file, $, imgElement) {
178
188
  let valueH = Math.trunc(valueW / ratio);
179
189
  while (valueW > options.image.srcset_min_width) {
180
190
  const src = imageSrc(`@${valueW}w`);
191
+ console.log(`Generating srcset ${src}`);
181
192
  const absoluteFilename = translateSrc(globalState.dir, path.dirname(file), src);
182
- const compressedData = await compressImage(originalData, { width: valueW, height: valueH });
183
- if (compressedData && !globalState.args.nowrite) {
184
- fs.writeFile(absoluteFilename, compressedData);
193
+ // Don't generate srcset file twice
194
+ if (!globalState.compressedFiles.has(absoluteFilename)) {
195
+ // Add file to list avoid recompression
196
+ globalState.compressedFiles.add(absoluteFilename);
197
+ const compressedImage = await compressImage(originalData, { width: valueW, height: valueH }, true);
198
+ if ((compressedImage === null || compressedImage === void 0 ? void 0 : compressedImage.data) && !globalState.args.nowrite) {
199
+ fs.writeFile(absoluteFilename, compressedImage.data);
200
+ }
185
201
  }
186
- // Add file to list avoid recompression
187
- globalState.compressedFiles.add(absoluteFilename);
188
202
  new_srcset += `, ${src} ${valueW}w`;
189
203
  // reduce size
190
204
  valueW -= step;
191
205
  valueH = Math.trunc(valueW / ratio);
192
206
  }
193
207
  if (new_srcset) {
194
- img.attr('srcset', `${attrib_src} ${w}w` + new_srcset);
208
+ img.attr('srcset', `${img.attr('src')} ${w}w` + new_srcset);
195
209
  }
196
210
  }
197
211
  }
@@ -261,8 +275,11 @@ function setImageSize(img, meta) {
261
275
  // Something when wrong
262
276
  throw new Error('Unexpected issue when resolving image sizes');
263
277
  }
264
- export async function optimize() {
265
- const paths = await globby('**/*.{htm,html}', { cwd: globalState.dir });
278
+ export async function optimize(exclude) {
279
+ const glob = ['**/*.{htm,html}'];
280
+ if (exclude)
281
+ glob.push('!' + exclude);
282
+ const paths = await globby(glob, { cwd: globalState.dir });
266
283
  // Sequential async
267
284
  await paths.reduce(async (previousPromise, item) => {
268
285
  await previousPromise;
package/dist/options.d.ts CHANGED
@@ -1,8 +1,28 @@
1
+ export declare type WebpOptions = {
2
+ effort?: number;
3
+ mode?: 'lossless' | 'lossly';
4
+ quality?: number;
5
+ };
1
6
  export declare type Options = {
2
7
  image: {
3
8
  embed_size: number;
4
9
  srcset_min_width: number;
5
10
  srcset_max_width: number;
11
+ jpeg: {
12
+ options?: {
13
+ quality?: number;
14
+ mozjpeg?: boolean;
15
+ };
16
+ };
17
+ png: {
18
+ options?: {
19
+ compressionLevel?: number;
20
+ };
21
+ };
22
+ webp: {
23
+ options_lossless?: WebpOptions;
24
+ options_lossly?: WebpOptions;
25
+ };
6
26
  };
7
27
  };
8
28
  declare const default_options: Options;
package/dist/options.js CHANGED
@@ -2,7 +2,30 @@ const default_options = {
2
2
  image: {
3
3
  embed_size: 400,
4
4
  srcset_min_width: 400,
5
- srcset_max_width: 5120
5
+ srcset_max_width: 5120,
6
+ jpeg: {
7
+ options: {
8
+ quality: 80,
9
+ mozjpeg: true
10
+ }
11
+ },
12
+ png: {
13
+ options: {
14
+ compressionLevel: 9,
15
+ }
16
+ },
17
+ webp: {
18
+ options_lossless: {
19
+ effort: 4,
20
+ quality: 80,
21
+ mode: 'lossless'
22
+ },
23
+ options_lossly: {
24
+ effort: 4,
25
+ quality: 80,
26
+ mode: 'lossly'
27
+ }
28
+ }
6
29
  }
7
30
  };
8
31
  export default default_options;
@@ -0,0 +1,25 @@
1
+ export declare type Args = {
2
+ nowrite?: boolean;
3
+ };
4
+ export declare type Result = {
5
+ file: string;
6
+ originalSize: number;
7
+ compressedSize: number;
8
+ };
9
+ export declare type Summary = {
10
+ nbFiles: number;
11
+ nbFilesCompressed: number;
12
+ dataLenUncompressed: number;
13
+ dataLenCompressed: number;
14
+ };
15
+ export declare class GlobalState {
16
+ dir: string;
17
+ args: Args;
18
+ compressedFiles: Set<string>;
19
+ compressedFilesResult: Result[];
20
+ summary: Summary;
21
+ summaryByExtension: Record<string, Summary>;
22
+ addFile(r: Result): void;
23
+ }
24
+ declare const state: GlobalState;
25
+ export default state;
@@ -0,0 +1,45 @@
1
+ import path from "path";
2
+ export class GlobalState {
3
+ constructor() {
4
+ this.dir = 'dist';
5
+ this.args = {};
6
+ this.compressedFiles = new Set();
7
+ this.compressedFilesResult = [];
8
+ this.summary = {
9
+ nbFiles: 0,
10
+ nbFilesCompressed: 0,
11
+ dataLenCompressed: 0,
12
+ dataLenUncompressed: 0
13
+ };
14
+ this.summaryByExtension = {};
15
+ }
16
+ addFile(r) {
17
+ const isCompressed = r.compressedSize < r.originalSize ? 1 : 0;
18
+ this.compressedFiles.add(r.file);
19
+ this.compressedFilesResult.push(r);
20
+ this.summary.nbFiles++;
21
+ state.summary.nbFilesCompressed += isCompressed;
22
+ state.summary.dataLenUncompressed += r.originalSize;
23
+ state.summary.dataLenCompressed += r.compressedSize;
24
+ const ext = path.extname(r.file);
25
+ if (ext) {
26
+ let summary = this.summaryByExtension[ext];
27
+ if (!summary) {
28
+ summary = {
29
+ nbFiles: 0,
30
+ nbFilesCompressed: 0,
31
+ dataLenUncompressed: 0,
32
+ dataLenCompressed: 0
33
+ };
34
+ this.summaryByExtension[ext] = summary;
35
+ }
36
+ summary.nbFiles++;
37
+ summary.nbFilesCompressed += isCompressed;
38
+ summary.dataLenUncompressed += r.originalSize;
39
+ summary.dataLenCompressed += r.compressedSize;
40
+ }
41
+ }
42
+ ;
43
+ }
44
+ const state = new GlobalState();
45
+ export default state;
package/dist/state.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  export declare type Args = {
2
2
  nowrite?: boolean;
3
3
  };
4
- export declare type Result = {
5
- file: string;
4
+ export declare type ReportItem = {
5
+ action: string;
6
6
  originalSize: number;
7
7
  compressedSize: number;
8
8
  };
@@ -16,10 +16,10 @@ export declare class GlobalState {
16
16
  dir: string;
17
17
  args: Args;
18
18
  compressedFiles: Set<string>;
19
- compressedFilesResult: Result[];
19
+ optimizedFiles: Set<string>;
20
20
  summary: Summary;
21
21
  summaryByExtension: Record<string, Summary>;
22
- addFile(r: Result): void;
22
+ reportItem(r: ReportItem): void;
23
23
  }
24
24
  declare const state: GlobalState;
25
25
  export default state;
package/dist/state.js CHANGED
@@ -1,10 +1,9 @@
1
- import path from "path";
2
1
  export class GlobalState {
3
2
  constructor() {
4
3
  this.dir = 'dist';
5
4
  this.args = {};
6
5
  this.compressedFiles = new Set();
7
- this.compressedFilesResult = [];
6
+ this.optimizedFiles = new Set();
8
7
  this.summary = {
9
8
  nbFiles: 0,
10
9
  nbFilesCompressed: 0,
@@ -13,17 +12,14 @@ export class GlobalState {
13
12
  };
14
13
  this.summaryByExtension = {};
15
14
  }
16
- addFile(r) {
15
+ reportItem(r) {
17
16
  const isCompressed = r.compressedSize < r.originalSize ? 1 : 0;
18
- this.compressedFiles.add(r.file);
19
- this.compressedFilesResult.push(r);
20
17
  this.summary.nbFiles++;
21
18
  state.summary.nbFilesCompressed += isCompressed;
22
19
  state.summary.dataLenUncompressed += r.originalSize;
23
20
  state.summary.dataLenCompressed += r.compressedSize;
24
- const ext = path.extname(r.file);
25
- if (ext) {
26
- let summary = this.summaryByExtension[ext];
21
+ if (r.action) {
22
+ let summary = this.summaryByExtension[r.action];
27
23
  if (!summary) {
28
24
  summary = {
29
25
  nbFiles: 0,
@@ -31,7 +27,7 @@ export class GlobalState {
31
27
  dataLenUncompressed: 0,
32
28
  dataLenCompressed: 0
33
29
  };
34
- this.summaryByExtension[ext] = summary;
30
+ this.summaryByExtension[r.action] = summary;
35
31
  }
36
32
  summary.nbFiles++;
37
33
  summary.nbFilesCompressed += isCompressed;
@@ -0,0 +1,6 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ export declare type ImageFormat = 'webp' | 'svg' | 'jpg' | 'png' | undefined;
3
+ export declare type Image = {
4
+ format: ImageFormat;
5
+ data: Buffer;
6
+ };
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@divriots/jampack",
3
- "version": "0.1.8",
3
+ "version": "0.2.1",
4
4
  "description": "Packer for static websites",
5
5
  "author": "Georges Gomes @georges-gomes",
6
6
  "bin": {