webpack-spritesmith
Version:
Webpack plugin that converts set of images into a spritesheet and SASS/LESS/Stylus mixins
174 lines (142 loc) • 6.23 kB
JavaScript
;
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, 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; }
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); }
function _iterableToArrayLimit(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
const _ = require('lodash');
const path = require('path');
const fs = require('fs');
const templater = require('spritesheet-templates');
const fThrowExpectField = f => {
throw `Expected field "${f}" in options of SpritesmithPlugin`;
};
module.exports = rawOptions => {
rawOptions.src || fThrowExpectField('src');
rawOptions.src.cwd || fThrowExpectField('src.cwd');
rawOptions.src.glob || fThrowExpectField('src.glob');
rawOptions.target || fThrowExpectField('target');
rawOptions.target.css || fThrowExpectField('target.css');
rawOptions.target.image || fThrowExpectField('target.image');
const mergedOptions = _.merge({
src: {
options: {}
},
logCreatedFiles: false,
apiOptions: {
generateSpriteName: fileName => path.parse(path.relative(mergedOptions.src.cwd, fileName)).name,
cssImageRef: rawOptions.target.image,
customTemplates: {},
handlebarsHelpers: {}
},
spritesmithOptions: {},
spritesheetTemplatesOptions: {}
}, rawOptions);
mergedOptions.target.css = normalizeTargetCss(mergedOptions);
mergedOptions.target.css.forEach((css, i) => {
if (!css[1].format) {
throw 'SpritesmithPlugin was unable to derive ' + `css format from extension "${path.parse(css[0] || '').ext}" ` + `in "target.css[${i}]" and format was not specified explicitly`;
}
});
_.forEach(mergedOptions.customTemplates, (template, templateName) => {
if (typeof template === 'string') {
templater.addHandlebarsTemplate(templateName, fs.readFileSync(template, 'utf-8'));
} else if (typeof template === 'function') {
templater.addTemplate(templateName, template);
} else {
throw new Error('custom template can be either path/to/handlebars/template or actual template function');
}
});
const handlebarsHelpers = mergedOptions.apiOptions.handlebarsHelpers;
Object.keys(handlebarsHelpers).forEach(helperKey => {
templater.registerHandlebarsHelper(helperKey, handlebarsHelpers[helperKey]);
});
processRetinaOptions(mergedOptions);
return mergedOptions;
};
const normalizeTargetCss = mergedOptions => {
let css = mergedOptions.target.css;
if (!Array.isArray(css)) {
css = [[css, mergedOptions.spritesheetTemplatesOptions]];
}
return css.map((css, i) => {
if (typeof css === 'string') {
return [css, {
format: extractFormatFromCSSFilename(css)
}];
}
if (Array.isArray(css)) {
const _css$slice = css.slice(0),
_css$slice2 = _slicedToArray(_css$slice, 2),
cssFileName = _css$slice2[0],
_css$slice2$ = _css$slice2[1],
options = _css$slice2$ === void 0 ? {} : _css$slice2$;
const format = options.format || extractFormatFromCSSFilename(cssFileName);
return [cssFileName, _objectSpread({}, options, {
format
})];
}
throw new Error(`'target.css[${i}] must be String or Array'`);
});
};
const extensionToCssFormat = {
'.stylus': 'stylus',
'.styl': 'stylus',
'.sass': 'sass',
'.scss': 'scss',
'.less': 'less',
'.json': 'json',
'.css': 'css'
};
const extractFormatFromCSSFilename = cssFileName => extensionToCssFormat[path.parse(cssFileName).ext];
const processRetinaOptions = options => {
if (!('retina' in options)) {
return;
}
if (typeof options.retina === 'string') {
const suffix = options.retina;
const r = options.retina = {
classifier: suffixToClassifier(suffix)
};
r.targetImage = addSuffixToFileName(suffix, options.target.image, path);
r.cssImageRef = addSuffixToFileName(suffix, options.apiOptions.cssImageRef, path.posix);
} else {
options.retina.classifier || fThrowExpectField('retina.classifier');
options.retina.targetImage || fThrowExpectField('retina.targetImage');
options.retina.cssImageRef || fThrowExpectField('retina.cssImageRef');
}
options.target.css.forEach(css => {
css[1].format += '_retina';
});
};
const suffixToClassifier = suffix => fileName => {
const parsed = splitExt(fileName);
if (endsWith(suffix, parsed.name)) {
return {
type: 'retina',
retinaName: fileName,
normalName: parsed.name.slice(0, -suffix.length) + parsed.ext
};
} else {
return {
type: 'normal',
retinaName: parsed.name + suffix + parsed.ext,
normalName: fileName
};
}
};
const endsWith = (suffix, str) => str.slice(-suffix.length) === suffix;
const splitExt = fileName => {
const extInd = fileName.lastIndexOf('.');
return {
name: fileName.slice(0, extInd),
ext: fileName.slice(extInd)
};
};
const addSuffixToFileName = (suffix, fileName, pathImpl) => {
const parsed = pathImpl.parse(fileName);
parsed.name += suffix;
parsed.base = parsed.name + parsed.ext;
return pathImpl.format(parsed);
};