forked from robb0wen/eleventy-plugin-local-images
-
Notifications
You must be signed in to change notification settings - Fork 0
/
.eleventy.js
121 lines (96 loc) · 3.54 KB
/
.eleventy.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
const fs = require('fs-extra');
const path = require('path');
const { JSDOM } = require('jsdom');
const fetch = require('node-fetch');
const sh = require('shorthash');
const fileType = require('file-type');
let config = { distPath: '_site', verbose: false, attribute: 'src' };
const downloadImage = async path => {
if (config.verbose) {
console.log('eleventy-plugin-local-images: Attempting to copy ' + path);
}
try {
const imgBuffer = await fetch(path)
.then(res => {
if (res.status == 200) {
return res;
} else {
throw new Error(`File "${path}" not found`);
}
}).then(res => res.buffer());
return imgBuffer
} catch (error) {
console.log(error);
}
}
const getFileType = (filename, buffer) => {
// infer the file ext from the buffer
const type = fileType(buffer);
if (type.ext) {
// return the filename with extension
return `${filename}.${type.ext}`;
} else {
throw new Error(`Couldn't infer file extension for "${path}"`);
}
};
const urlJoin = (a, b) => `${a.replace(/\/$/, '')}/${b.replace(/^\//, '')}`;
const processImage = async img => {
let { distPath, assetPath, attribute } = config;
const external = /https?:\/\/((?:[\w\d-]+\.)+[\w\d]{2,})/i;
const attr = attribute.split(",").map(attr => attr.trim()).find(attr => img.getAttribute(attr));
const imgPath = img.getAttribute(attr);
if (external.test(imgPath)) {
try {
// get the filname from the path
const pathComponents = imgPath.split('/');
// break off cache busting string if there is one
let filename = pathComponents[pathComponents.length - 1].split("?");
filename = filename[0];
// generate a unique short hash based on the original file path
// this will prevent filename clashes
const hash = sh.unique(imgPath);
// image is external so download it.
let imgBuffer = await downloadImage(imgPath);
if (imgBuffer) {
// check if the remote image has a file extension and then hash the filename
const hashedFilename = !path.extname(filename) ? `${hash}-${getFileType(filename, imgBuffer)}` : `${hash}-${filename}`;
// create the file path from config
let outputFilePath = path.join(distPath,assetPath, hashedFilename);
// save the file out, and log it to the console
await fs.outputFile(outputFilePath, imgBuffer);
if (config.verbose) {
console.log(`eleventy-plugin-local-images: Saving ${filename} to ${outputFilePath}`);
}
// Update the image with the new file path
img.setAttribute(attr, urlJoin(assetPath, hashedFilename));
}
} catch (error) {
console.log(error);
}
}
return img;
};
const grabRemoteImages = async (rawContent, outputPath) => {
let { selector = 'img' } = config;
let content = rawContent;
if (outputPath.endsWith('.html')) {
const dom = new JSDOM(content);
const images = [...dom.window.document.querySelectorAll(selector)];
if (images.length > 0) {
await Promise.all(images.map(i => processImage(i)));
content = dom.serialize();
}
}
return content;
};
module.exports = {
initArguments: {},
configFunction: async (eleventyConfig, pluginOptions = {}) => {
config = Object.assign({}, config, pluginOptions);
// check the required config is present
if (!config.assetPath || !config.distPath) {
throw new Error("eleventy-plugin-local-images requires that assetPath and distPath are set");
}
eleventyConfig.addTransform('localimages', grabRemoteImages);
},
};