react-native-svg-asset-plugin
Version:
Asset plugin for importing SVG images in React Native
144 lines (115 loc) • 5.12 kB
JavaScript
;
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
const fse = require('fs-extra');
const path = require('path');
const cache = require('./cache');
const config = require('./config');
const sharp = require('./sharp');
const funcUtils = require('./utils/func');
const fsUtils = require('./utils/fs');
const asyncConfig = config.load();
async function reactNativeSvgAssetPlugin(assetData) {
const filePath = assetData.files.length ? assetData.files[0] : '';
if (await shouldConvertFile(assetData, filePath)) {
return convertSvg(assetData);
} else {
return assetData;
}
}
async function shouldConvertFile(assetData, filePath) {
if (assetData.type !== 'svg') {
return false;
}
const ignoreRegex = (await asyncConfig).ignoreRegex;
if (ignoreRegex && ignoreRegex.test(filePath)) {
return false;
}
return true;
}
async function convertSvg(assetData) {
if (assetData.scales.length !== assetData.files.length) {
throw new Error("Passed scales doesn't match passed files.");
} else if (assetData.files.length === 0) {
throw new Error('No files passed.');
} else if (assetData.files.length > 1) {
throw new Error('Multiple SVG scales not supported.');
} else if (assetData.scales[0] !== 1) {
throw new Error('Scaled SVGs not supported.');
}
const inputFilePath = assetData.files[0];
const inputFileScale = assetData.scales[0];
const config = await asyncConfig;
const outputDirectory = path.join(assetData.fileSystemLocation, config.cacheDir);
const outputName = `${assetData.name}-${assetData.hash}`;
await fse.ensureDir(outputDirectory);
const imageLoader = createimageLoader(inputFilePath);
const outputImages = await Promise.all(config.scales.map(imageScale => ensurePngUpToDate(imageLoader, imageScale / inputFileScale, path.join(outputDirectory, `${outputName}${getScaleSuffix(imageScale)}.png`), config.output)));
return _objectSpread(_objectSpread({}, assetData), {}, {
fileSystemLocation: outputDirectory,
httpServerLocation: `${assetData.httpServerLocation}/${config.cacheDir}`,
files: outputImages.map(outputImage => outputImage.filePath),
scales: outputImages.map(outputImage => outputImage.scale),
name: outputName,
type: 'png'
});
}
/**
* Creates an image loader for input file.
* This provides lazy cached loading of image data.
*/
function createimageLoader(inputFilePath) {
return funcUtils.memo(async () => {
const [fileBuffer, loadedSharp] = await Promise.all([fse.readFile(inputFilePath), sharp.load()]);
const metadata = await loadedSharp(fileBuffer).metadata();
return {
buffer: fileBuffer,
metadata: metadata
};
});
}
/**
* Ensures that the resultign PNG file exists on the fileystem.
*
* In case the file does not exist yet, or it is older than the
* current configuration, it will be generated.
*
* Otherwise the existing file will be left in place, and its
* last modified time will be updated.
*/
async function ensurePngUpToDate(imageLoader, scale, outputFilePath, outputOptions) {
if (await cache.isFileOutdated(outputFilePath, await asyncConfig)) {
const inputFile = await imageLoader();
await generatePng(inputFile, scale, outputFilePath, outputOptions);
} else {
await fsUtils.updateLastModifiedTime(outputFilePath);
}
return {
filePath: outputFilePath,
scale: scale
};
}
/**
* Generates a PNG file from a loaded SVG file.
*/
async function generatePng(inputFile, scale, outputFilePath, outputOptions) {
if (inputFile.metadata.density === undefined) {
throw new Error('Input image missing density information');
}
const density = inputFile.metadata.density;
const loadedSharp = await sharp.load();
await loadedSharp(inputFile.buffer, {
density: density * scale
}).png(outputOptions).toFile(outputFilePath);
}
function getScaleSuffix(scale) {
switch (scale) {
case 1:
return '';
default:
return `@${scale}x`;
}
}
const assetDataPlugin = reactNativeSvgAssetPlugin;
module.exports = assetDataPlugin;