@divriots/jampack 0.2.3 → 0.2.4
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/index.js +3 -3
- package/dist/optimize.js +91 -46
- package/dist/state.d.ts +1 -0
- package/dist/utils/resource.d.ts +31 -0
- package/dist/utils/resource.js +106 -0
- package/dist/utils.d.ts +0 -2
- package/dist/utils.js +0 -13
- package/package.json +2 -1
- package/dist/compressors/image.d.ts +0 -1
- package/dist/compressors/image.js +0 -1
- package/dist/optimizers/img.d.ts +0 -1
- package/dist/optimizers/img.js +0 -1
- package/dist/options.d.ts +0 -30
- package/dist/options.js +0 -59
- package/dist/state copy.d.ts +0 -25
- package/dist/state copy.js +0 -45
package/dist/index.js
CHANGED
|
@@ -94,13 +94,13 @@ function printIssues() {
|
|
|
94
94
|
printTitle('Issues', kleur.bgRed);
|
|
95
95
|
console.log('');
|
|
96
96
|
for (let [file, list] of $state.issues) {
|
|
97
|
-
console.log(
|
|
97
|
+
console.log('▶ ' + file + '\n');
|
|
98
98
|
list.forEach(issue => {
|
|
99
99
|
issueCount++;
|
|
100
|
-
console.log(
|
|
100
|
+
console.log(`${kleur.bgYellow(` ${issue.type} `)} ${issue.msg}\n`);
|
|
101
101
|
});
|
|
102
102
|
}
|
|
103
|
+
printTitle(`${issueCount} issue(s) over ${$state.issues.size} files`, kleur.bgYellow);
|
|
103
104
|
console.log('');
|
|
104
|
-
printTitle(`${issueCount} issue(s) over ${$state.issues.size} files`, kleur.bgRed);
|
|
105
105
|
}
|
|
106
106
|
}
|
package/dist/optimize.js
CHANGED
|
@@ -2,14 +2,14 @@ import { globby } from 'globby';
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import * as fs from 'fs/promises';
|
|
4
4
|
import cheerio from 'cheerio';
|
|
5
|
-
import {
|
|
6
|
-
import sharp from "sharp";
|
|
5
|
+
import { isNumeric } from './utils.js';
|
|
7
6
|
import config from './config.js';
|
|
8
7
|
import { compressImage } from './compress.js';
|
|
9
8
|
import svgToMiniDataURI from 'mini-svg-data-uri';
|
|
10
9
|
import $state from './state.js';
|
|
11
10
|
import kleur from 'kleur';
|
|
12
11
|
import ora from 'ora';
|
|
12
|
+
import { isLocal, Resource, translateSrc } from './utils/resource.js';
|
|
13
13
|
async function analyse(file) {
|
|
14
14
|
console.log('▶ ' + file);
|
|
15
15
|
const html = (await fs.readFile(path.join($state.dir, file))).toString();
|
|
@@ -49,7 +49,7 @@ async function processImage(htmlfile, $, imgElement) {
|
|
|
49
49
|
*/
|
|
50
50
|
const attrib_src = img.attr('src');
|
|
51
51
|
if (!attrib_src) {
|
|
52
|
-
$state.reportIssue(htmlfile, { msg: `Missing [src] on img - processing skipped.` });
|
|
52
|
+
$state.reportIssue(htmlfile, { type: 'warn', msg: `Missing [src] on img - processing skipped.` });
|
|
53
53
|
return;
|
|
54
54
|
}
|
|
55
55
|
/*
|
|
@@ -57,7 +57,7 @@ async function processImage(htmlfile, $, imgElement) {
|
|
|
57
57
|
*/
|
|
58
58
|
const attrib_alt = img.attr('alt');
|
|
59
59
|
if (attrib_alt === undefined) {
|
|
60
|
-
$state.reportIssue(htmlfile, { msg: `Missing [alt] on img src="${attrib_src}" - Adding alt="" meanwhile.` });
|
|
60
|
+
$state.reportIssue(htmlfile, { type: 'a11y', msg: `Missing [alt] on img src="${attrib_src}" - Adding alt="" meanwhile.` });
|
|
61
61
|
img.attr('alt', "");
|
|
62
62
|
}
|
|
63
63
|
if (attrib_src.startsWith('data:')) {
|
|
@@ -65,10 +65,6 @@ async function processImage(htmlfile, $, imgElement) {
|
|
|
65
65
|
// TODO: try to compress it
|
|
66
66
|
return;
|
|
67
67
|
}
|
|
68
|
-
if (!isLocal(attrib_src)) {
|
|
69
|
-
// Image not local, don't touch it
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
68
|
/*
|
|
73
69
|
* Attribute 'loading'
|
|
74
70
|
*/
|
|
@@ -85,49 +81,46 @@ async function processImage(htmlfile, $, imgElement) {
|
|
|
85
81
|
// Don't touch it
|
|
86
82
|
break;
|
|
87
83
|
default:
|
|
88
|
-
$state.reportIssue(htmlfile, { msg: `Invalid [loading]="${attr_loading}" on img src="${attrib_src}"` });
|
|
84
|
+
$state.reportIssue(htmlfile, { type: 'invalid', msg: `Invalid [loading]="${attr_loading}" on img src="${attrib_src}"` });
|
|
89
85
|
}
|
|
90
86
|
/*
|
|
91
87
|
* Attribute 'decoding'
|
|
92
88
|
*/
|
|
93
89
|
img.attr('decoding', 'async');
|
|
90
|
+
if (!isLocal(attrib_src)) {
|
|
91
|
+
// Image not local, don't touch it
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
94
|
/*
|
|
95
95
|
* Loading image
|
|
96
96
|
*/
|
|
97
|
-
const
|
|
98
|
-
// file
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
catch (e) {
|
|
103
|
-
$state.reportIssue(htmlfile, { msg: `Can't find img src="${attrib_src}" in ${absoluteImgPath}` });
|
|
97
|
+
const originalImage = await Resource.loadResource($state.dir, htmlfile, attrib_src);
|
|
98
|
+
// No file -> give up
|
|
99
|
+
if (!originalImage) {
|
|
100
|
+
$state.reportIssue(htmlfile, { type: 'erro', msg: `Can't find img on disk src="${attrib_src}"` });
|
|
104
101
|
return;
|
|
105
102
|
}
|
|
106
|
-
// Read file
|
|
107
|
-
const originalData = await fs.readFile(absoluteImgPath);
|
|
108
|
-
const sharpFile = sharp(absoluteImgPath);
|
|
109
|
-
const meta = await sharpFile.metadata();
|
|
110
103
|
/*
|
|
111
104
|
* Compress image to WebP
|
|
112
105
|
*/
|
|
113
106
|
let newImage;
|
|
114
|
-
if (!$state.optimizedFiles.has(
|
|
107
|
+
if (!$state.optimizedFiles.has(originalImage.filePathAbsolute)) {
|
|
115
108
|
// Let's avoid to optimize same images twice
|
|
116
|
-
$state.optimizedFiles.add(
|
|
117
|
-
newImage = await compressImage(
|
|
118
|
-
if ((newImage === null || newImage === void 0 ? void 0 : newImage.data) && newImage.data.length <
|
|
109
|
+
$state.optimizedFiles.add(originalImage.filePathAbsolute);
|
|
110
|
+
newImage = await compressImage(await originalImage.getData(), {}, true);
|
|
111
|
+
if ((newImage === null || newImage === void 0 ? void 0 : newImage.data) && newImage.data.length < await originalImage.getLen()) {
|
|
119
112
|
// Do we need to add an new extension?
|
|
120
113
|
const newExtension = `.${newImage.format}`;
|
|
121
|
-
const additionalExtension = path.extname(
|
|
122
|
-
const newFilename =
|
|
114
|
+
const additionalExtension = path.extname(originalImage.filePathAbsolute) === newExtension ? '' : newExtension;
|
|
115
|
+
const newFilename = originalImage.filePathAbsolute + additionalExtension;
|
|
123
116
|
if (!$state.args.nowrite) {
|
|
124
117
|
fs.writeFile(newFilename, newImage.data);
|
|
125
118
|
}
|
|
126
119
|
$state.compressedFiles.add(newFilename);
|
|
127
120
|
// Report compression result
|
|
128
121
|
$state.reportSummary({
|
|
129
|
-
action: (newFilename !==
|
|
130
|
-
originalSize:
|
|
122
|
+
action: (newFilename !== originalImage.filePathAbsolute) ? `${await originalImage.getExt()}->${newImage.format}` : path.extname(originalImage.filePathAbsolute),
|
|
123
|
+
originalSize: await originalImage.getLen(),
|
|
131
124
|
compressedSize: newImage.data.length
|
|
132
125
|
});
|
|
133
126
|
img.attr('src', attrib_src + additionalExtension);
|
|
@@ -160,17 +153,13 @@ async function processImage(htmlfile, $, imgElement) {
|
|
|
160
153
|
img.attr('src', datauri);
|
|
161
154
|
img.removeAttr('loading');
|
|
162
155
|
img.removeAttr('decoding');
|
|
163
|
-
$state.reportSummary({ action: `${newImage.format}->embed`, originalSize:
|
|
156
|
+
$state.reportSummary({ action: `${newImage.format}->embed`, originalSize: await originalImage.getLen(), compressedSize: newImage.data.length });
|
|
164
157
|
}
|
|
165
158
|
}
|
|
166
159
|
/*
|
|
167
160
|
* Attribute 'width' & 'height'
|
|
168
161
|
*/
|
|
169
|
-
const [w, h] = setImageSize(img,
|
|
170
|
-
if (w < 0 || h < 0) {
|
|
171
|
-
$state.reportIssue(htmlfile, { msg: `Unexpected error in image size calculation src="${attrib_src}" - can't performance some optmizations.` });
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
162
|
+
const [w, h] = await setImageSize(img, originalImage);
|
|
174
163
|
if (isEmbed) {
|
|
175
164
|
// Image is embed, no need for more processing
|
|
176
165
|
return;
|
|
@@ -178,8 +167,9 @@ async function processImage(htmlfile, $, imgElement) {
|
|
|
178
167
|
//
|
|
179
168
|
// Stop here if svg
|
|
180
169
|
//
|
|
181
|
-
if (
|
|
170
|
+
if (await originalImage.getExt() === 'svg') {
|
|
182
171
|
return;
|
|
172
|
+
}
|
|
183
173
|
/*
|
|
184
174
|
* Attribute 'srcset'
|
|
185
175
|
*/
|
|
@@ -208,7 +198,7 @@ async function processImage(htmlfile, $, imgElement) {
|
|
|
208
198
|
if (!$state.compressedFiles.has(absoluteFilename)) {
|
|
209
199
|
// Add file to list avoid recompression
|
|
210
200
|
$state.compressedFiles.add(absoluteFilename);
|
|
211
|
-
const compressedImage = await compressImage(
|
|
201
|
+
const compressedImage = await compressImage(await originalImage.getData(), { width: valueW, height: valueH }, true);
|
|
212
202
|
if ((compressedImage === null || compressedImage === void 0 ? void 0 : compressedImage.data) && !$state.args.nowrite) {
|
|
213
203
|
fs.writeFile(absoluteFilename, compressedImage.data);
|
|
214
204
|
}
|
|
@@ -224,10 +214,10 @@ async function processImage(htmlfile, $, imgElement) {
|
|
|
224
214
|
}
|
|
225
215
|
}
|
|
226
216
|
catch (e) {
|
|
227
|
-
$state.reportIssue(htmlfile, { msg: `Unexpected error while processing image: ${JSON.stringify(e)}` });
|
|
217
|
+
$state.reportIssue(htmlfile, { type: 'erro', msg: e.message || `Unexpected error while processing image: ${JSON.stringify(e)}` });
|
|
228
218
|
}
|
|
229
219
|
}
|
|
230
|
-
function setImageSize(img,
|
|
220
|
+
async function setImageSize(img, image) {
|
|
231
221
|
let width = img.attr('width');
|
|
232
222
|
let height = img.attr('height');
|
|
233
223
|
let width_new = undefined;
|
|
@@ -246,8 +236,12 @@ function setImageSize(img, meta) {
|
|
|
246
236
|
}
|
|
247
237
|
}
|
|
248
238
|
// If we don't have the metadata, we can't do much more
|
|
249
|
-
|
|
250
|
-
|
|
239
|
+
const meta = await image.getImageMeta();
|
|
240
|
+
if (!meta) {
|
|
241
|
+
throw new Error(`Can't get image meta information of "${image.src}" - some optimizations are not possible without this information.`);
|
|
242
|
+
}
|
|
243
|
+
if (meta.width === undefined && meta.height === undefined) {
|
|
244
|
+
throw new Error(`Can't get image width and height of "${image.src}" - some optimizations are not possible without this information.`);
|
|
251
245
|
}
|
|
252
246
|
const originalRatio = meta.width / meta.height;
|
|
253
247
|
if (width !== undefined && height !== undefined) {
|
|
@@ -274,17 +268,68 @@ function setImageSize(img, meta) {
|
|
|
274
268
|
}
|
|
275
269
|
else {
|
|
276
270
|
// No width or height provided - set both to image size
|
|
277
|
-
|
|
278
|
-
|
|
271
|
+
if (await image.getExt() === 'svg') {
|
|
272
|
+
// svg with no height and width has special sizing by browsers
|
|
273
|
+
// They size it inside 300x150 unless they have width and height
|
|
274
|
+
// attributes
|
|
275
|
+
// Load svg
|
|
276
|
+
const c = cheerio.load(await image.getData(), {});
|
|
277
|
+
const svg = c('svg').first();
|
|
278
|
+
const svg_viewbox = svg.attr('viewbox'); // bug in cheerio here, should be "viewBox"
|
|
279
|
+
const svg_width = svg.attr('width');
|
|
280
|
+
const svg_height = svg.attr('height');
|
|
281
|
+
// Calculate aspect ratio from viewbox
|
|
282
|
+
let svg_aspectratio_from_viewbox = undefined;
|
|
283
|
+
if (svg_viewbox) {
|
|
284
|
+
const box = svg_viewbox.split(' ');
|
|
285
|
+
const w = parseInt(box[2], 10);
|
|
286
|
+
const h = parseInt(box[3], 10);
|
|
287
|
+
svg_aspectratio_from_viewbox = w / h;
|
|
288
|
+
}
|
|
289
|
+
// Set size
|
|
290
|
+
//
|
|
291
|
+
if (svg_width && isNumeric(svg_width) && svg_height && isNumeric(svg_height)) {
|
|
292
|
+
// height and width are present
|
|
293
|
+
// use them
|
|
294
|
+
width_new = parseInt(svg_width, 10);
|
|
295
|
+
height_new = parseInt(svg_height, 10);
|
|
296
|
+
}
|
|
297
|
+
else if (svg_width === undefined && svg_height === undefined && svg_aspectratio_from_viewbox) {
|
|
298
|
+
// no height and no width but viewbox is present
|
|
299
|
+
// fit it in default browser box 300x150
|
|
300
|
+
if (svg_aspectratio_from_viewbox >= 2) {
|
|
301
|
+
// fit width
|
|
302
|
+
width_new = 300;
|
|
303
|
+
height_new = width_new / svg_aspectratio_from_viewbox;
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
// fit height
|
|
307
|
+
height_new = 150;
|
|
308
|
+
width_new = height_new * svg_aspectratio_from_viewbox;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
// no width and no height and no viewbox
|
|
313
|
+
// default browser values
|
|
314
|
+
width_new = 300;
|
|
315
|
+
height_new = 150;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
width_new = meta.width;
|
|
320
|
+
height_new = meta.height;
|
|
321
|
+
}
|
|
279
322
|
}
|
|
280
323
|
// New sizes
|
|
281
324
|
if (width_new !== undefined && height_new !== undefined) {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
325
|
+
const result_w = Math.round(width_new);
|
|
326
|
+
const result_h = Math.round(height_new);
|
|
327
|
+
img.attr('width', result_w.toFixed(0));
|
|
328
|
+
img.attr('height', result_h.toFixed(0));
|
|
329
|
+
return [result_w, result_h];
|
|
285
330
|
}
|
|
286
331
|
// Something when wrong
|
|
287
|
-
throw new Error(
|
|
332
|
+
throw new Error(`Unexpected issue when resolving image size "${image.src}"`);
|
|
288
333
|
}
|
|
289
334
|
export async function optimize(exclude) {
|
|
290
335
|
const glob = ['**/*.{htm,html}'];
|
package/dist/state.d.ts
CHANGED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
+
import { FileExtension, MimeType } from 'file-type';
|
|
3
|
+
declare type ImageMeta = {
|
|
4
|
+
width: number | undefined;
|
|
5
|
+
height: number | undefined;
|
|
6
|
+
isProgressive: boolean;
|
|
7
|
+
hasAlpha: boolean;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* File extension, mime and data are loaded only on demand, then cached.
|
|
11
|
+
*/
|
|
12
|
+
export declare class Resource {
|
|
13
|
+
readonly src: string;
|
|
14
|
+
readonly filePathAbsolute: string;
|
|
15
|
+
private ext;
|
|
16
|
+
private mime;
|
|
17
|
+
private data;
|
|
18
|
+
private image_meta;
|
|
19
|
+
constructor(src: string, filePathAbsolute: string);
|
|
20
|
+
getData(): Promise<Buffer>;
|
|
21
|
+
getImageMeta(): Promise<ImageMeta | null | undefined>;
|
|
22
|
+
getLen(): Promise<number>;
|
|
23
|
+
getExt(): Promise<FileExtension | 'svg'>;
|
|
24
|
+
getMime(): Promise<MimeType | 'image/svg+xml'>;
|
|
25
|
+
private loadFileType;
|
|
26
|
+
static loadResource(projectRoot: string, relativeFile: string, src: string): Promise<Resource | undefined>;
|
|
27
|
+
}
|
|
28
|
+
export declare function isLocal(src: string): boolean;
|
|
29
|
+
export declare function fileExists(path: string): Promise<boolean>;
|
|
30
|
+
export declare function translateSrc(projectRoot: string, htmlRelativePath: string, src: string): string;
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import * as url from 'url';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as fs from 'fs/promises';
|
|
4
|
+
import { fileTypeFromBuffer } from 'file-type';
|
|
5
|
+
import sharp from 'sharp';
|
|
6
|
+
/**
|
|
7
|
+
* File extension, mime and data are loaded only on demand, then cached.
|
|
8
|
+
*/
|
|
9
|
+
export class Resource {
|
|
10
|
+
constructor(src, filePathAbsolute) {
|
|
11
|
+
this.src = src;
|
|
12
|
+
this.filePathAbsolute = filePathAbsolute;
|
|
13
|
+
}
|
|
14
|
+
async getData() {
|
|
15
|
+
if (this.data === undefined) {
|
|
16
|
+
this.data = await fs.readFile(this.filePathAbsolute);
|
|
17
|
+
}
|
|
18
|
+
return this.data;
|
|
19
|
+
}
|
|
20
|
+
async getImageMeta() {
|
|
21
|
+
if (this.image_meta === undefined) {
|
|
22
|
+
const ext = await this.getExt();
|
|
23
|
+
switch (ext) {
|
|
24
|
+
case 'svg':
|
|
25
|
+
case 'gif':
|
|
26
|
+
case 'jpg':
|
|
27
|
+
case 'png':
|
|
28
|
+
case 'webp':
|
|
29
|
+
case 'tif':
|
|
30
|
+
let sharpFile = await sharp(await this.getData(), { animated: true });
|
|
31
|
+
const meta = await sharpFile.metadata();
|
|
32
|
+
this.image_meta = {
|
|
33
|
+
width: meta.width,
|
|
34
|
+
height: meta.height,
|
|
35
|
+
isProgressive: meta.isProgressive || false,
|
|
36
|
+
hasAlpha: meta.hasAlpha || false
|
|
37
|
+
};
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (!this.image_meta) {
|
|
42
|
+
console.log(await this.getExt());
|
|
43
|
+
}
|
|
44
|
+
return this.image_meta;
|
|
45
|
+
}
|
|
46
|
+
async getLen() {
|
|
47
|
+
return (await this.getData()).length;
|
|
48
|
+
}
|
|
49
|
+
async getExt() {
|
|
50
|
+
if (this.ext === undefined) {
|
|
51
|
+
await this.loadFileType();
|
|
52
|
+
}
|
|
53
|
+
return this.ext;
|
|
54
|
+
}
|
|
55
|
+
async getMime() {
|
|
56
|
+
if (this.mime === undefined) {
|
|
57
|
+
await this.loadFileType();
|
|
58
|
+
}
|
|
59
|
+
return this.mime;
|
|
60
|
+
}
|
|
61
|
+
async loadFileType() {
|
|
62
|
+
const fileType = await fileTypeFromBuffer(await this.getData());
|
|
63
|
+
if (this.filePathAbsolute.endsWith('.svg')) {
|
|
64
|
+
this.ext = 'svg';
|
|
65
|
+
this.mime = 'image/svg+xml';
|
|
66
|
+
}
|
|
67
|
+
else if (fileType) {
|
|
68
|
+
this.ext = fileType.ext;
|
|
69
|
+
this.mime = fileType.mime;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
throw new Error(`Unknown file type "${this.src}"`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
static async loadResource(projectRoot, relativeFile, src) {
|
|
76
|
+
if (!isLocal(src)) {
|
|
77
|
+
throw new Error('Source should be local');
|
|
78
|
+
}
|
|
79
|
+
const relativePath = path.join(projectRoot, src.startsWith('/') ? '' : path.dirname(relativeFile), src);
|
|
80
|
+
const absolutePath = path.resolve(relativePath);
|
|
81
|
+
if (await fileExists(absolutePath)) {
|
|
82
|
+
return new Resource(src, absolutePath);
|
|
83
|
+
}
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
export function isLocal(src) {
|
|
88
|
+
const u = url.parse(src);
|
|
89
|
+
return !u.host;
|
|
90
|
+
}
|
|
91
|
+
export async function fileExists(path) {
|
|
92
|
+
try {
|
|
93
|
+
await fs.stat(path);
|
|
94
|
+
}
|
|
95
|
+
catch (e) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
export function translateSrc(projectRoot, htmlRelativePath, src) {
|
|
101
|
+
if (!isLocal(src)) {
|
|
102
|
+
throw new Error('Source should be local');
|
|
103
|
+
}
|
|
104
|
+
const srcAbsolutePath = path.join(projectRoot, src.startsWith('/') ? '' : htmlRelativePath, src);
|
|
105
|
+
return path.resolve(srcAbsolutePath);
|
|
106
|
+
}
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,4 +1,2 @@
|
|
|
1
1
|
export declare function formatBytes(bytes: number, decimals?: number): string;
|
|
2
|
-
export declare function isLocal(src: string): boolean;
|
|
3
|
-
export declare function translateSrc(projectRoot: string, htmlRelativePath: string, src: string): string;
|
|
4
2
|
export declare function isNumeric(value: string): boolean;
|
package/dist/utils.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import * as url from 'url';
|
|
2
|
-
import * as path from 'path';
|
|
3
1
|
export function formatBytes(bytes, decimals = 2) {
|
|
4
2
|
if (!+bytes)
|
|
5
3
|
return '0 Bytes';
|
|
@@ -9,17 +7,6 @@ export function formatBytes(bytes, decimals = 2) {
|
|
|
9
7
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
10
8
|
return `${(bytes / Math.pow(k, i)).toFixed(dm)} ${sizes[i]}`;
|
|
11
9
|
}
|
|
12
|
-
export function isLocal(src) {
|
|
13
|
-
const u = url.parse(src);
|
|
14
|
-
return !u.host;
|
|
15
|
-
}
|
|
16
|
-
export function translateSrc(projectRoot, htmlRelativePath, src) {
|
|
17
|
-
if (!isLocal(src)) {
|
|
18
|
-
throw new Error('Source should be local');
|
|
19
|
-
}
|
|
20
|
-
const srcAbsolutePath = path.join(projectRoot, src.startsWith('/') ? '' : htmlRelativePath, src);
|
|
21
|
-
return path.resolve(srcAbsolutePath);
|
|
22
|
-
}
|
|
23
10
|
export function isNumeric(value) {
|
|
24
11
|
return /^\d+$/.test(value);
|
|
25
12
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@divriots/jampack",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "Packer for static websites",
|
|
5
5
|
"author": "Georges Gomes @georges-gomes",
|
|
6
6
|
"bin": {
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"cheerio": "0.22.0",
|
|
30
30
|
"commander": "^9.4.1",
|
|
31
31
|
"csso": "^5.0.5",
|
|
32
|
+
"file-type": "^18.0.0",
|
|
32
33
|
"globby": "^13.1.2",
|
|
33
34
|
"html-minifier-terser": "^7.0.0",
|
|
34
35
|
"kleur": "^4.1.5",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/optimizers/img.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/optimizers/img.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/options.d.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
export declare type WebpOptions = {
|
|
2
|
-
effort: number;
|
|
3
|
-
mode: 'lossless' | 'lossly';
|
|
4
|
-
quality: number;
|
|
5
|
-
};
|
|
6
|
-
export declare type Options = {
|
|
7
|
-
image: {
|
|
8
|
-
embed_size: number;
|
|
9
|
-
srcset_min_width: number;
|
|
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
|
-
};
|
|
26
|
-
};
|
|
27
|
-
};
|
|
28
|
-
declare const default_options: Options;
|
|
29
|
-
export declare function fast(): void;
|
|
30
|
-
export default default_options;
|
package/dist/options.js
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
const default_options = {
|
|
2
|
-
image: {
|
|
3
|
-
embed_size: 400,
|
|
4
|
-
srcset_min_width: 400,
|
|
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
|
-
}
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
const fast_options_override = {
|
|
32
|
-
image: {
|
|
33
|
-
embed_size: 0,
|
|
34
|
-
srcset_min_width: 1024,
|
|
35
|
-
srcset_max_width: 1024,
|
|
36
|
-
jpeg: {
|
|
37
|
-
options: {
|
|
38
|
-
mozjpeg: false
|
|
39
|
-
}
|
|
40
|
-
},
|
|
41
|
-
png: {
|
|
42
|
-
options: {
|
|
43
|
-
compressionLevel: 0,
|
|
44
|
-
}
|
|
45
|
-
},
|
|
46
|
-
webp: {
|
|
47
|
-
options_lossless: {
|
|
48
|
-
effort: 0,
|
|
49
|
-
},
|
|
50
|
-
options_lossly: {
|
|
51
|
-
effort: 0,
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
export function fast() {
|
|
57
|
-
Object.assign(default_options, fast_options_override);
|
|
58
|
-
}
|
|
59
|
-
export default default_options;
|
package/dist/state copy.d.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
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;
|
package/dist/state copy.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
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;
|