Files
30-seconds-of-code/node_modules/gatsby-plugin-sharp/trace-svg.js
2019-08-20 15:52:05 +02:00

159 lines
3.7 KiB
JavaScript

"use strict";
const {
promisify
} = require(`bluebird`);
const crypto = require(`crypto`);
const _ = require(`lodash`);
const tmpDir = require(`os`).tmpdir();
const sharp = require(`./safe-sharp`);
const duotone = require(`./duotone`);
const {
getPluginOptions,
healOptions
} = require(`./plugin-options`);
const {
reportError
} = require(`./report-error`);
exports.notMemoizedPrepareTraceSVGInputFile = async ({
file,
options,
tmpFilePath,
reporter
}) => {
let pipeline;
try {
pipeline = sharp(file.absolutePath);
if (!options.rotate) {
pipeline.rotate();
}
} catch (err) {
reportError(`Failed to process image ${file.absolutePath}`, err, reporter);
return;
}
pipeline.resize(options.width, options.height, {
position: options.cropFocus
}).png({
compressionLevel: options.pngCompressionLevel,
adaptiveFiltering: false,
force: options.toFormat === `png`
}).jpeg({
quality: options.quality,
progressive: options.jpegProgressive,
force: options.toFormat === `jpg`
}); // grayscale
if (options.grayscale) {
pipeline = pipeline.grayscale();
} // rotate
if (options.rotate && options.rotate !== 0) {
pipeline = pipeline.rotate(options.rotate);
} // duotone
if (options.duotone) {
pipeline = await duotone(options.duotone, options.toFormat, pipeline);
}
await new Promise((resolve, reject) => pipeline.toFile(tmpFilePath, err => {
if (err) {
return reject(err);
}
return resolve();
}));
};
const optimize = svg => {
const SVGO = require(`svgo`);
const svgo = new SVGO({
multipass: true,
floatPrecision: 0
});
return svgo.optimize(svg).then(({
data
}) => data);
};
exports.notMemoizedtraceSVG = async ({
file,
args,
fileArgs,
reporter
}) => {
const options = healOptions(getPluginOptions(), Object.assign({}, fileArgs && fileArgs.maxWidth && fileArgs.maxHeight ? {
height: fileArgs.maxHeight,
width: fileArgs.maxWidth
} : {}, fileArgs), file.extension);
const tmpFilePath = `${tmpDir}/${file.internal.contentDigest}-${file.name}-${crypto.createHash(`md5`).update(JSON.stringify(options)).digest(`hex`)}.${file.extension}`;
try {
await exports.memoizedPrepareTraceSVGInputFile({
tmpFilePath,
file,
options,
reporter
});
const svgToMiniDataURI = require(`mini-svg-data-uri`);
const potrace = require(`potrace`);
const trace = promisify(potrace.trace);
const defaultArgs = {
color: `lightgray`,
optTolerance: 0.4,
turdSize: 100,
turnPolicy: potrace.Potrace.TURNPOLICY_MAJORITY
};
const optionsSVG = _.defaults(args, defaultArgs); // `srcset` attribute rejects URIs with literal spaces
const encodeSpaces = str => str.replace(/ /gi, `%20`);
return trace(tmpFilePath, optionsSVG).then(optimize).then(svgToMiniDataURI).then(encodeSpaces);
} catch (e) {
throw e;
}
};
let memoizedPrepareTraceSVGInputFile, memoizedTraceSVG;
const createMemoizedFunctions = () => {
exports.memoizedPrepareTraceSVGInputFile = memoizedPrepareTraceSVGInputFile = _.memoize(exports.notMemoizedPrepareTraceSVGInputFile, ({
tmpFilePath
}) => tmpFilePath);
exports.memoizedTraceSVG = memoizedTraceSVG = _.memoize(exports.notMemoizedtraceSVG, ({
file,
args,
fileArgs
}) => `${file.internal.contentDigest}${JSON.stringify(args)}${JSON.stringify(fileArgs)}`);
}; // This is very hacky, but memoized function are pretty tricky to spy on
// in tests ;(
createMemoizedFunctions();
exports.createMemoizedFunctions = () => {
createMemoizedFunctions();
};
exports.clearMemoizeCaches = () => {
memoizedTraceSVG.cache.clear();
memoizedPrepareTraceSVGInputFile.cache.clear();
};