@divriots/jampack 0.11.7 → 0.12.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/README.md +23 -4
- package/dist/cache.d.ts +8 -4
- package/dist/cache.js +14 -13
- package/dist/compressors/images.js +7 -4
- package/dist/index.js +10 -0
- package/dist/optimize.js +18 -10
- package/dist/optimizers/img-external.d.ts +1 -0
- package/dist/optimizers/img-external.js +99 -0
- package/dist/utils/cache-control-parser.d.ts +16 -0
- package/dist/utils/cache-control-parser.js +95 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -49,7 +49,11 @@ becomes responsive with dimensions:
|
|
|
49
49
|
|
|
50
50
|
```html
|
|
51
51
|
<img src="./redpanda.jpg.webp" alt="Red panda"
|
|
52
|
-
srcset="
|
|
52
|
+
srcset="
|
|
53
|
+
./redpanda.jpg.webp 3872w, ./redpanda@3572w.jpg.webp 3572w, ./redpanda@3272w.jpg.webp 3272w,
|
|
54
|
+
./redpanda@2972w.jpg.webp 2972w, ./redpanda@2672w.jpg.webp 2672w, ./redpanda@2372w.jpg.webp 2372w,
|
|
55
|
+
./redpanda@2072w.jpg.webp 2072w, ./redpanda@1772w.jpg.webp 1772w, ./redpanda@1472w.jpg.webp 1472w,
|
|
56
|
+
./redpanda@1172w.jpg.webp 1172w, ./redpanda@872w.jpg.webp 872w"
|
|
53
57
|
sizes="100vw"
|
|
54
58
|
loading="lazy"
|
|
55
59
|
decoding="async"
|
|
@@ -69,9 +73,24 @@ becomes responsive with multiple formats including AVIF:
|
|
|
69
73
|
|
|
70
74
|
```html
|
|
71
75
|
<picture>
|
|
72
|
-
<source type="image/avif"
|
|
73
|
-
|
|
74
|
-
|
|
76
|
+
<source type="image/avif"
|
|
77
|
+
srcset="./redpanda@1936w.avif 1936w, ./redpanda@1636w.avif 1636w,
|
|
78
|
+
./redpanda@1336w.avif 1336w, ./redpanda@1036w.avif 1036w,
|
|
79
|
+
./redpanda@736w.avif 736w">
|
|
80
|
+
<source type="image/webp"
|
|
81
|
+
srcset="./redpanda@1936w.webp 1936w, ./redpanda@1636w.webp 1636w,
|
|
82
|
+
./redpanda@1336w.webp 1336w, ./redpanda@1036w.webp 1036w,
|
|
83
|
+
./redpanda@736w.webp 736w">
|
|
84
|
+
<img src="./redpanda.jpg"
|
|
85
|
+
alt="Red panda"
|
|
86
|
+
loading="lazy"
|
|
87
|
+
decoding="async"
|
|
88
|
+
width="1936"
|
|
89
|
+
height="1296"
|
|
90
|
+
srcset="./redpanda.jpg 1936w, ./redpanda@1636w.jpg 1636w,
|
|
91
|
+
./redpanda@1336w.jpg 1336w, ./redpanda@1036w.jpg 1036w,
|
|
92
|
+
./redpanda@736w.jpg 736w"
|
|
93
|
+
sizes="100vw">
|
|
75
94
|
</picture>
|
|
76
95
|
```
|
|
77
96
|
|
package/dist/cache.d.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
-
|
|
2
|
+
export type Category = 'img' | 'img-ext';
|
|
3
|
+
export type CacheData = {
|
|
4
|
+
buffer: Buffer;
|
|
5
|
+
meta: any;
|
|
6
|
+
};
|
|
3
7
|
declare function cleanCache(full: boolean): Promise<void>;
|
|
4
8
|
declare function computeCacheHash(buffer: Buffer, options?: any): string;
|
|
5
|
-
declare function
|
|
6
|
-
declare function
|
|
7
|
-
export { cleanCache, computeCacheHash,
|
|
9
|
+
declare function getFromCache(category: Category, hash: string): Promise<CacheData | undefined>;
|
|
10
|
+
declare function addToCache(category: Category, hash: string, data: CacheData): Promise<void>;
|
|
11
|
+
export { cleanCache, computeCacheHash, getFromCache, addToCache };
|
package/dist/cache.js
CHANGED
|
@@ -4,6 +4,7 @@ import * as fs from 'fs/promises';
|
|
|
4
4
|
import { VERSION } from './version.js';
|
|
5
5
|
import $state from './state.js';
|
|
6
6
|
const CACHE_FOLDER = '.jampack/cache';
|
|
7
|
+
const CACHE_FOLDER_VERSION = CACHE_FOLDER + '/' + VERSION;
|
|
7
8
|
async function cleanCache(full) {
|
|
8
9
|
if (full) {
|
|
9
10
|
try {
|
|
@@ -32,37 +33,37 @@ function computeCacheHash(buffer, options) {
|
|
|
32
33
|
if ($state.args.nocache) {
|
|
33
34
|
return '';
|
|
34
35
|
}
|
|
35
|
-
let hash = `${
|
|
36
|
+
let hash = `${hasha(buffer, { algorithm: 'sha256' })}`;
|
|
36
37
|
if (options) {
|
|
37
38
|
hash += '/' + hasha(JSON.stringify(options), { algorithm: 'md5' });
|
|
38
39
|
}
|
|
39
40
|
return hash;
|
|
40
41
|
}
|
|
41
|
-
function getLocation(hash) {
|
|
42
|
-
return path.join(
|
|
42
|
+
function getLocation(hash, category) {
|
|
43
|
+
return path.join(CACHE_FOLDER_VERSION, category, hash);
|
|
43
44
|
}
|
|
44
|
-
async function
|
|
45
|
+
async function getFromCache(category, hash) {
|
|
45
46
|
if ($state.args.nocache) {
|
|
46
47
|
return undefined;
|
|
47
48
|
}
|
|
48
|
-
const location = getLocation(hash);
|
|
49
|
+
const location = getLocation(hash, category);
|
|
49
50
|
try {
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
return {
|
|
51
|
+
const buffer = await fs.readFile(path.join(location, 'data'));
|
|
52
|
+
const meta = JSON.parse((await fs.readFile(path.join(location, 'meta'))).toString());
|
|
53
|
+
return { buffer, meta };
|
|
53
54
|
}
|
|
54
55
|
catch (e) {
|
|
55
56
|
// Problem during cache loading or not in cache
|
|
56
57
|
}
|
|
57
58
|
return undefined;
|
|
58
59
|
}
|
|
59
|
-
async function
|
|
60
|
+
async function addToCache(category, hash, data) {
|
|
60
61
|
if ($state.args.nocache) {
|
|
61
62
|
return;
|
|
62
63
|
}
|
|
63
|
-
const location = getLocation(hash);
|
|
64
|
+
const location = getLocation(hash, category);
|
|
64
65
|
await fs.mkdir(location, { recursive: true });
|
|
65
|
-
await fs.writeFile(path.join(location, 'data'),
|
|
66
|
-
await fs.writeFile(path.join(location, '
|
|
66
|
+
await fs.writeFile(path.join(location, 'data'), data.buffer);
|
|
67
|
+
await fs.writeFile(path.join(location, 'meta'), JSON.stringify(data.meta));
|
|
67
68
|
}
|
|
68
|
-
export { cleanCache, computeCacheHash,
|
|
69
|
+
export { cleanCache, computeCacheHash, getFromCache, addToCache };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import sharp from 'sharp';
|
|
2
2
|
import { optimize as svgo } from 'svgo';
|
|
3
|
-
import {
|
|
3
|
+
import { getFromCache, addToCache, computeCacheHash } from '../cache.js';
|
|
4
4
|
import config from '../config.js';
|
|
5
5
|
export const AllImageFormat = ['webp', 'svg', 'jpg', 'png', 'avif'];
|
|
6
6
|
function createWebpOptions(opt) {
|
|
@@ -13,9 +13,9 @@ function createWebpOptions(opt) {
|
|
|
13
13
|
export async function compressImage(data, options) {
|
|
14
14
|
var _a, _b;
|
|
15
15
|
const cacheHash = computeCacheHash(data, options);
|
|
16
|
-
const imageFromCache = await
|
|
16
|
+
const imageFromCache = await getFromCache('img', cacheHash);
|
|
17
17
|
if (imageFromCache) {
|
|
18
|
-
return imageFromCache;
|
|
18
|
+
return { data: imageFromCache.buffer, format: imageFromCache.meta };
|
|
19
19
|
}
|
|
20
20
|
// Load modifiable toFormat
|
|
21
21
|
let toFormat = options.toFormat || 'unchanged';
|
|
@@ -128,7 +128,10 @@ export async function compressImage(data, options) {
|
|
|
128
128
|
format: outputFormat,
|
|
129
129
|
data: await sharpFile.toBuffer(),
|
|
130
130
|
};
|
|
131
|
-
await
|
|
131
|
+
await addToCache('img', cacheHash, {
|
|
132
|
+
buffer: outputImage.data,
|
|
133
|
+
meta: outputImage.format,
|
|
134
|
+
});
|
|
132
135
|
// Go
|
|
133
136
|
return outputImage;
|
|
134
137
|
}
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,8 @@ import { exit } from 'process';
|
|
|
11
11
|
import kleur from 'kleur';
|
|
12
12
|
import { cleanCache } from './cache.js';
|
|
13
13
|
import { VERSION } from './version.js';
|
|
14
|
+
import { mkdirSync } from 'fs';
|
|
15
|
+
import { join } from 'path';
|
|
14
16
|
const logo = ` __ __
|
|
15
17
|
|__|____ _____ ___________ ____ | | __
|
|
16
18
|
| \\__ \\ / \\\\____ \\__ \\ _/ ___\\| |/ /
|
|
@@ -54,6 +56,14 @@ program
|
|
|
54
56
|
}
|
|
55
57
|
// Clean cache
|
|
56
58
|
cleanCache(options.cleancache);
|
|
59
|
+
// Create _jampack folder
|
|
60
|
+
try {
|
|
61
|
+
mkdirSync(join($state.dir, '_jampack'));
|
|
62
|
+
}
|
|
63
|
+
catch (e) {
|
|
64
|
+
console.error(e.message);
|
|
65
|
+
exit(1);
|
|
66
|
+
}
|
|
57
67
|
if (!options.onlycomp) {
|
|
58
68
|
printTitle('PASS 1 - Optimizing');
|
|
59
69
|
console.time('Done');
|
package/dist/optimize.js
CHANGED
|
@@ -10,6 +10,7 @@ import $state from './state.js';
|
|
|
10
10
|
import kleur from 'kleur';
|
|
11
11
|
import ora from 'ora';
|
|
12
12
|
import { isLocal, Resource, translateSrc } from './utils/resource.js';
|
|
13
|
+
import { downloadExternalImage } from './optimizers/img-external.js';
|
|
13
14
|
async function analyse(file) {
|
|
14
15
|
var _a;
|
|
15
16
|
console.log('▶ ' + file);
|
|
@@ -122,7 +123,7 @@ async function processImage(htmlfile, $, imgElement, isAboveTheFold) {
|
|
|
122
123
|
/*
|
|
123
124
|
* Attribute 'src'
|
|
124
125
|
*/
|
|
125
|
-
|
|
126
|
+
let attrib_src = img.attr('src');
|
|
126
127
|
if (!attrib_src) {
|
|
127
128
|
const attrib_data_src = img.attr('data-src');
|
|
128
129
|
if (attrib_data_src) {
|
|
@@ -189,18 +190,25 @@ async function processImage(htmlfile, $, imgElement, isAboveTheFold) {
|
|
|
189
190
|
*/
|
|
190
191
|
if (!isLocal(attrib_src)) {
|
|
191
192
|
switch (config.image.external.process) {
|
|
192
|
-
case 'off':
|
|
193
|
-
// Don't process external images
|
|
193
|
+
case 'off': // Don't process external images
|
|
194
194
|
return;
|
|
195
|
-
case 'download':
|
|
196
|
-
|
|
195
|
+
case 'download': // Download external image for local processing
|
|
196
|
+
try {
|
|
197
|
+
attrib_src = await downloadExternalImage(attrib_src);
|
|
198
|
+
img.attr('src', attrib_src);
|
|
199
|
+
}
|
|
200
|
+
catch (e) {
|
|
201
|
+
$state.reportIssue(htmlfile, {
|
|
202
|
+
type: 'warn',
|
|
203
|
+
msg: `Failed to download image: ${attrib_src} - ${e.message}`,
|
|
204
|
+
});
|
|
205
|
+
return; // No more processing
|
|
206
|
+
}
|
|
197
207
|
break;
|
|
198
|
-
case '
|
|
199
|
-
// Use unpic to use CDN capabilities
|
|
200
|
-
throw new Error('External images with `cdn-srcset-when-possible` is not implemented yet.');
|
|
201
|
-
case 'add-dimensions-only':
|
|
202
|
-
// Only add dimensions of image
|
|
208
|
+
case 'add-dimensions-only': // Only add dimensions of image
|
|
203
209
|
throw new Error('External images with `add-dimensions-only` is not implemented yet.');
|
|
210
|
+
case 'cdn-srcset-when-possible': // Use unpic to use CDN capabilities
|
|
211
|
+
throw new Error('External images with `cdn-srcset-when-possible` is not implemented yet.');
|
|
204
212
|
}
|
|
205
213
|
}
|
|
206
214
|
/*
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function downloadExternalImage(href: string): Promise<string>;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import * as path from 'path';
|
|
2
|
+
import * as fs from 'fs/promises';
|
|
3
|
+
import hasha from 'hasha';
|
|
4
|
+
import { fileTypeFromBuffer } from 'file-type';
|
|
5
|
+
import { addToCache, getFromCache } from '../cache.js';
|
|
6
|
+
import { parse } from '../utils/cache-control-parser.js';
|
|
7
|
+
import $state from '../state.js';
|
|
8
|
+
export async function downloadExternalImage(href) {
|
|
9
|
+
var _a, _b, _c, _d, _e;
|
|
10
|
+
const hash = hasha(href, {
|
|
11
|
+
algorithm: 'md5',
|
|
12
|
+
});
|
|
13
|
+
// Default with values from cache
|
|
14
|
+
let buffer;
|
|
15
|
+
let ext;
|
|
16
|
+
// Image is in cache?
|
|
17
|
+
const dataFromCache = await getFromCache('img-ext', hash);
|
|
18
|
+
const cacheControl = (_a = dataFromCache === null || dataFromCache === void 0 ? void 0 : dataFromCache.meta) === null || _a === void 0 ? void 0 : _a.CacheControl;
|
|
19
|
+
if (dataFromCache && cacheControl) {
|
|
20
|
+
const date = (_b = dataFromCache === null || dataFromCache === void 0 ? void 0 : dataFromCache.meta) === null || _b === void 0 ? void 0 : _b.Date;
|
|
21
|
+
const maxAge = cacheControl['max-age'];
|
|
22
|
+
const expires = (_c = dataFromCache === null || dataFromCache === void 0 ? void 0 : dataFromCache.meta) === null || _c === void 0 ? void 0 : _c.Expires;
|
|
23
|
+
let expireTime = NaN;
|
|
24
|
+
if (date && maxAge) {
|
|
25
|
+
expireTime = Date.parse(date) + maxAge * 1000;
|
|
26
|
+
}
|
|
27
|
+
else if (expires) {
|
|
28
|
+
expireTime = Date.parse(expires);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
// No max-age with Date and no expires
|
|
32
|
+
// No information for cache validity
|
|
33
|
+
// We have to make a request
|
|
34
|
+
}
|
|
35
|
+
// Is cache still valid?
|
|
36
|
+
if (expireTime && Date.now() < expireTime) {
|
|
37
|
+
buffer = dataFromCache.buffer;
|
|
38
|
+
ext = dataFromCache.meta.Extension;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// No buffer, then no cache hit so let's HTTP request
|
|
42
|
+
if (!buffer) {
|
|
43
|
+
const lastdate = (_d = dataFromCache === null || dataFromCache === void 0 ? void 0 : dataFromCache.meta) === null || _d === void 0 ? void 0 : _d.Date;
|
|
44
|
+
const fetchOption = lastdate
|
|
45
|
+
? { headers: { 'If-Modified-Since': lastdate } }
|
|
46
|
+
: {};
|
|
47
|
+
console.log('Downloading ' + href);
|
|
48
|
+
const resp = await fetch(href, fetchOption);
|
|
49
|
+
switch (resp.status) {
|
|
50
|
+
case 200: // New image
|
|
51
|
+
const responseBuffer = await resp.arrayBuffer();
|
|
52
|
+
// Detect extension
|
|
53
|
+
ext = (_e = (await fileTypeFromBuffer(responseBuffer))) === null || _e === void 0 ? void 0 : _e.ext;
|
|
54
|
+
buffer = Buffer.from(responseBuffer);
|
|
55
|
+
// Did the server requested no cache?
|
|
56
|
+
const cacheControl = parse(resp.headers.get('Cache-Control') || '');
|
|
57
|
+
const maxAge = cacheControl['max-age'];
|
|
58
|
+
if (cacheControl['no-store'] ||
|
|
59
|
+
cacheControl['no-cache'] ||
|
|
60
|
+
cacheControl['must-revalidate'] ||
|
|
61
|
+
(maxAge !== undefined && maxAge < 1)) {
|
|
62
|
+
// No cache requested
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
// Add downloaded image to cache
|
|
66
|
+
await addToCache('img-ext', hash, {
|
|
67
|
+
buffer,
|
|
68
|
+
meta: {
|
|
69
|
+
Date: resp.headers.get('Date'),
|
|
70
|
+
Extension: ext,
|
|
71
|
+
CacheControl: cacheControl,
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
break;
|
|
76
|
+
case 304: // Not modified - image in cache is good to use
|
|
77
|
+
if (dataFromCache) {
|
|
78
|
+
buffer = dataFromCache.buffer;
|
|
79
|
+
ext = dataFromCache.meta.Extension;
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
// This is not possible
|
|
83
|
+
throw new Error('Assert error: 304 responses but no data in cache');
|
|
84
|
+
}
|
|
85
|
+
break;
|
|
86
|
+
default: // Otherwise something is wrong
|
|
87
|
+
throw new Error(resp.statusText);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// buffer can't be undefined here
|
|
91
|
+
if (!buffer)
|
|
92
|
+
throw new Error('Buffer is undefined');
|
|
93
|
+
// Construct local filename
|
|
94
|
+
if (!ext)
|
|
95
|
+
throw new Error('Unknown image format');
|
|
96
|
+
const filename = `/_jampack/${hash}.${ext}`;
|
|
97
|
+
fs.writeFile(path.join($state.dir, filename), buffer);
|
|
98
|
+
return filename;
|
|
99
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface CacheControl {
|
|
2
|
+
'max-age'?: number;
|
|
3
|
+
's-maxage'?: number;
|
|
4
|
+
'stale-while-revalidate'?: number;
|
|
5
|
+
'stale-if-error'?: number;
|
|
6
|
+
public?: boolean;
|
|
7
|
+
private?: boolean;
|
|
8
|
+
'no-store'?: boolean;
|
|
9
|
+
'no-cache'?: boolean;
|
|
10
|
+
'must-revalidate'?: boolean;
|
|
11
|
+
'proxy-revalidate'?: boolean;
|
|
12
|
+
immutable?: boolean;
|
|
13
|
+
'no-transform'?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare const parse: (cacheControlHeader: string) => CacheControl;
|
|
16
|
+
export declare const stringify: (cacheControl: CacheControl) => string;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// Copied from https://github.com/etienne-martin/cache-control-parser (MIT licence)
|
|
2
|
+
// The npm package was not ESM - can't be used
|
|
3
|
+
const SUPPORTED_DIRECTIVES = [
|
|
4
|
+
'max-age',
|
|
5
|
+
's-maxage',
|
|
6
|
+
'stale-while-revalidate',
|
|
7
|
+
'stale-if-error',
|
|
8
|
+
'public',
|
|
9
|
+
'private',
|
|
10
|
+
'no-store',
|
|
11
|
+
'no-cache',
|
|
12
|
+
'must-revalidate',
|
|
13
|
+
'proxy-revalidate',
|
|
14
|
+
'immutable',
|
|
15
|
+
'no-transform',
|
|
16
|
+
];
|
|
17
|
+
export const parse = (cacheControlHeader) => {
|
|
18
|
+
const cacheControl = {};
|
|
19
|
+
const directives = cacheControlHeader
|
|
20
|
+
.toLowerCase()
|
|
21
|
+
.split(',')
|
|
22
|
+
.map((str) => str
|
|
23
|
+
.trim()
|
|
24
|
+
.split('=')
|
|
25
|
+
.map((str) => str.trim()));
|
|
26
|
+
for (const [directive, value] of directives) {
|
|
27
|
+
switch (directive) {
|
|
28
|
+
case 'max-age':
|
|
29
|
+
const maxAge = parseInt(value, 10);
|
|
30
|
+
if (isNaN(maxAge))
|
|
31
|
+
continue;
|
|
32
|
+
cacheControl['max-age'] = maxAge;
|
|
33
|
+
break;
|
|
34
|
+
case 's-maxage':
|
|
35
|
+
const sharedMaxAge = parseInt(value, 10);
|
|
36
|
+
if (isNaN(sharedMaxAge))
|
|
37
|
+
continue;
|
|
38
|
+
cacheControl['s-maxage'] = sharedMaxAge;
|
|
39
|
+
break;
|
|
40
|
+
case 'stale-while-revalidate':
|
|
41
|
+
const staleWhileRevalidate = parseInt(value, 10);
|
|
42
|
+
if (isNaN(staleWhileRevalidate))
|
|
43
|
+
continue;
|
|
44
|
+
cacheControl['stale-while-revalidate'] = staleWhileRevalidate;
|
|
45
|
+
break;
|
|
46
|
+
case 'stale-if-error':
|
|
47
|
+
const staleIfError = parseInt(value, 10);
|
|
48
|
+
if (isNaN(staleIfError))
|
|
49
|
+
continue;
|
|
50
|
+
cacheControl['stale-if-error'] = staleIfError;
|
|
51
|
+
break;
|
|
52
|
+
case 'public':
|
|
53
|
+
cacheControl.public = true;
|
|
54
|
+
break;
|
|
55
|
+
case 'private':
|
|
56
|
+
cacheControl.private = true;
|
|
57
|
+
break;
|
|
58
|
+
case 'no-store':
|
|
59
|
+
cacheControl['no-store'] = true;
|
|
60
|
+
break;
|
|
61
|
+
case 'no-cache':
|
|
62
|
+
cacheControl['no-cache'] = true;
|
|
63
|
+
break;
|
|
64
|
+
case 'must-revalidate':
|
|
65
|
+
cacheControl['must-revalidate'] = true;
|
|
66
|
+
break;
|
|
67
|
+
case 'proxy-revalidate':
|
|
68
|
+
cacheControl['proxy-revalidate'] = true;
|
|
69
|
+
break;
|
|
70
|
+
case 'immutable':
|
|
71
|
+
cacheControl.immutable = true;
|
|
72
|
+
break;
|
|
73
|
+
case 'no-transform':
|
|
74
|
+
cacheControl['no-transform'] = true;
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return cacheControl;
|
|
79
|
+
};
|
|
80
|
+
export const stringify = (cacheControl) => {
|
|
81
|
+
const directives = [];
|
|
82
|
+
for (const [key, value] of Object.entries(cacheControl)) {
|
|
83
|
+
if (!SUPPORTED_DIRECTIVES.includes(key))
|
|
84
|
+
continue;
|
|
85
|
+
switch (typeof value) {
|
|
86
|
+
case 'boolean':
|
|
87
|
+
directives.push(`${key}`);
|
|
88
|
+
break;
|
|
89
|
+
case 'number':
|
|
90
|
+
directives.push(`${key}=${value}`);
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return directives.join(', ');
|
|
95
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@divriots/jampack",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Packer for static websites",
|
|
5
5
|
"author": "Georges Gomes @georges-gomes",
|
|
6
6
|
"bin": {
|
|
@@ -45,10 +45,11 @@
|
|
|
45
45
|
"watch": "pnpm build --watch",
|
|
46
46
|
"try": "shx rm -fr demo && cp -R demo_rf demo && node ./dist/index.js ./demo --nocache --onlyoptim",
|
|
47
47
|
"build": "shx rm -rf dist && tsc -b",
|
|
48
|
-
"lint": "eslint . --ext .ts --config .eslintrc"
|
|
48
|
+
"lint": "eslint . --ext .ts --config .eslintrc",
|
|
49
|
+
"prepublishOnly": "pnpm build"
|
|
49
50
|
},
|
|
50
51
|
"engines": {
|
|
51
|
-
"node": ">=
|
|
52
|
+
"node": ">=18.0.0"
|
|
52
53
|
},
|
|
53
54
|
"bugs": "https://github.com/divriots/jampack/issues",
|
|
54
55
|
"keywords": [],
|