@divriots/jampack 0.2.2 → 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 +10 -9
- package/dist/logger.js +1 -1
- package/dist/optimize.js +93 -47
- 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/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
|
@@ -45,14 +45,17 @@ program.command('pack', { isDefault: true })
|
|
|
45
45
|
await optimize(options.exclude);
|
|
46
46
|
console.timeEnd('Done');
|
|
47
47
|
}
|
|
48
|
-
if (!options.onlyoptim) {
|
|
48
|
+
if (!options.onlyoptim && !options.fast) {
|
|
49
49
|
printTitle('PASS 2 - Compressing the rest');
|
|
50
50
|
console.time('Done');
|
|
51
51
|
await compress(options.exclude);
|
|
52
52
|
console.timeEnd('Done');
|
|
53
53
|
}
|
|
54
54
|
printSummary();
|
|
55
|
-
|
|
55
|
+
printIssues();
|
|
56
|
+
if (options.failOnIssues && $state.issues.size > 0) {
|
|
57
|
+
exit(1);
|
|
58
|
+
}
|
|
56
59
|
});
|
|
57
60
|
program.parse();
|
|
58
61
|
function printSummary() {
|
|
@@ -82,7 +85,7 @@ function printSummary() {
|
|
|
82
85
|
console.log(table(dataTable, config));
|
|
83
86
|
}
|
|
84
87
|
}
|
|
85
|
-
function
|
|
88
|
+
function printIssues() {
|
|
86
89
|
let issueCount = 0;
|
|
87
90
|
if ($state.issues.size === 0) {
|
|
88
91
|
printTitle('✔ No issues');
|
|
@@ -91,15 +94,13 @@ function printWarningsAndErrors(options) {
|
|
|
91
94
|
printTitle('Issues', kleur.bgRed);
|
|
92
95
|
console.log('');
|
|
93
96
|
for (let [file, list] of $state.issues) {
|
|
94
|
-
console.log(
|
|
97
|
+
console.log('▶ ' + file + '\n');
|
|
95
98
|
list.forEach(issue => {
|
|
96
99
|
issueCount++;
|
|
97
|
-
console.log(`${
|
|
100
|
+
console.log(`${kleur.bgYellow(` ${issue.type} `)} ${issue.msg}\n`);
|
|
98
101
|
});
|
|
99
102
|
}
|
|
100
|
-
printTitle(`${issueCount} issue(s) over ${$state.issues.size} files`, kleur.
|
|
101
|
-
|
|
102
|
-
exit(1);
|
|
103
|
-
}
|
|
103
|
+
printTitle(`${issueCount} issue(s) over ${$state.issues.size} files`, kleur.bgYellow);
|
|
104
|
+
console.log('');
|
|
104
105
|
}
|
|
105
106
|
}
|
package/dist/logger.js
CHANGED
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}" -
|
|
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,48 +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
|
-
const sharpFile = sharp(absoluteImgPath);
|
|
107
|
-
const meta = await sharpFile.metadata();
|
|
108
|
-
const originalData = await fs.readFile(absoluteImgPath);
|
|
109
103
|
/*
|
|
110
104
|
* Compress image to WebP
|
|
111
105
|
*/
|
|
112
106
|
let newImage;
|
|
113
|
-
if (!$state.optimizedFiles.has(
|
|
107
|
+
if (!$state.optimizedFiles.has(originalImage.filePathAbsolute)) {
|
|
114
108
|
// Let's avoid to optimize same images twice
|
|
115
|
-
$state.optimizedFiles.add(
|
|
116
|
-
newImage = await compressImage(
|
|
117
|
-
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()) {
|
|
118
112
|
// Do we need to add an new extension?
|
|
119
113
|
const newExtension = `.${newImage.format}`;
|
|
120
|
-
const additionalExtension = path.extname(
|
|
121
|
-
const newFilename =
|
|
114
|
+
const additionalExtension = path.extname(originalImage.filePathAbsolute) === newExtension ? '' : newExtension;
|
|
115
|
+
const newFilename = originalImage.filePathAbsolute + additionalExtension;
|
|
122
116
|
if (!$state.args.nowrite) {
|
|
123
117
|
fs.writeFile(newFilename, newImage.data);
|
|
124
118
|
}
|
|
125
119
|
$state.compressedFiles.add(newFilename);
|
|
126
120
|
// Report compression result
|
|
127
121
|
$state.reportSummary({
|
|
128
|
-
action: (newFilename !==
|
|
129
|
-
originalSize:
|
|
122
|
+
action: (newFilename !== originalImage.filePathAbsolute) ? `${await originalImage.getExt()}->${newImage.format}` : path.extname(originalImage.filePathAbsolute),
|
|
123
|
+
originalSize: await originalImage.getLen(),
|
|
130
124
|
compressedSize: newImage.data.length
|
|
131
125
|
});
|
|
132
126
|
img.attr('src', attrib_src + additionalExtension);
|
|
@@ -159,9 +153,13 @@ async function processImage(htmlfile, $, imgElement) {
|
|
|
159
153
|
img.attr('src', datauri);
|
|
160
154
|
img.removeAttr('loading');
|
|
161
155
|
img.removeAttr('decoding');
|
|
162
|
-
$state.reportSummary({ action: `${newImage.format}->embed`, originalSize:
|
|
156
|
+
$state.reportSummary({ action: `${newImage.format}->embed`, originalSize: await originalImage.getLen(), compressedSize: newImage.data.length });
|
|
163
157
|
}
|
|
164
158
|
}
|
|
159
|
+
/*
|
|
160
|
+
* Attribute 'width' & 'height'
|
|
161
|
+
*/
|
|
162
|
+
const [w, h] = await setImageSize(img, originalImage);
|
|
165
163
|
if (isEmbed) {
|
|
166
164
|
// Image is embed, no need for more processing
|
|
167
165
|
return;
|
|
@@ -169,14 +167,7 @@ async function processImage(htmlfile, $, imgElement) {
|
|
|
169
167
|
//
|
|
170
168
|
// Stop here if svg
|
|
171
169
|
//
|
|
172
|
-
if (
|
|
173
|
-
return;
|
|
174
|
-
/*
|
|
175
|
-
* Attribute 'width' & 'height'
|
|
176
|
-
*/
|
|
177
|
-
const [w, h] = setImageSize(img, meta);
|
|
178
|
-
if (w < 0 || h < 0) {
|
|
179
|
-
$state.reportIssue(htmlfile, { msg: `Unexpected error in image size calculation src="${attrib_src}" - can't performance some optmizations.` });
|
|
170
|
+
if (await originalImage.getExt() === 'svg') {
|
|
180
171
|
return;
|
|
181
172
|
}
|
|
182
173
|
/*
|
|
@@ -207,7 +198,7 @@ async function processImage(htmlfile, $, imgElement) {
|
|
|
207
198
|
if (!$state.compressedFiles.has(absoluteFilename)) {
|
|
208
199
|
// Add file to list avoid recompression
|
|
209
200
|
$state.compressedFiles.add(absoluteFilename);
|
|
210
|
-
const compressedImage = await compressImage(
|
|
201
|
+
const compressedImage = await compressImage(await originalImage.getData(), { width: valueW, height: valueH }, true);
|
|
211
202
|
if ((compressedImage === null || compressedImage === void 0 ? void 0 : compressedImage.data) && !$state.args.nowrite) {
|
|
212
203
|
fs.writeFile(absoluteFilename, compressedImage.data);
|
|
213
204
|
}
|
|
@@ -223,10 +214,10 @@ async function processImage(htmlfile, $, imgElement) {
|
|
|
223
214
|
}
|
|
224
215
|
}
|
|
225
216
|
catch (e) {
|
|
226
|
-
$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)}` });
|
|
227
218
|
}
|
|
228
219
|
}
|
|
229
|
-
function setImageSize(img,
|
|
220
|
+
async function setImageSize(img, image) {
|
|
230
221
|
let width = img.attr('width');
|
|
231
222
|
let height = img.attr('height');
|
|
232
223
|
let width_new = undefined;
|
|
@@ -245,8 +236,12 @@ function setImageSize(img, meta) {
|
|
|
245
236
|
}
|
|
246
237
|
}
|
|
247
238
|
// If we don't have the metadata, we can't do much more
|
|
248
|
-
|
|
249
|
-
|
|
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.`);
|
|
250
245
|
}
|
|
251
246
|
const originalRatio = meta.width / meta.height;
|
|
252
247
|
if (width !== undefined && height !== undefined) {
|
|
@@ -273,17 +268,68 @@ function setImageSize(img, meta) {
|
|
|
273
268
|
}
|
|
274
269
|
else {
|
|
275
270
|
// No width or height provided - set both to image size
|
|
276
|
-
|
|
277
|
-
|
|
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
|
+
}
|
|
278
322
|
}
|
|
279
323
|
// New sizes
|
|
280
324
|
if (width_new !== undefined && height_new !== undefined) {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
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];
|
|
284
330
|
}
|
|
285
331
|
// Something when wrong
|
|
286
|
-
throw new Error(
|
|
332
|
+
throw new Error(`Unexpected issue when resolving image size "${image.src}"`);
|
|
287
333
|
}
|
|
288
334
|
export async function optimize(exclude) {
|
|
289
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",
|
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;
|