UNPKG

webpack-spritesmith

Version:

Webpack plugin that converts set of images into a spritesheet and SASS/LESS/Stylus mixins

167 lines (131 loc) 6.28 kB
"use strict"; function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } 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; } const path = require('path'); const gaze = require('gaze'); const fs = require('mz/fs'); const _require = require('./utils'), promiseCall = _require.promiseCall; const processOptions = require('./processOptions'); module.exports = class SpritesmithPlugin { constructor(options) { this.options = processOptions(options); this.useRetinaTemplate = 'retina' in this.options; this.cleanMetaOutput(); } cleanMetaOutput() { this.metaOutput = { warnings: [], errors: [] }; } _hook(compiler, v3Name, v4Name, cb) { if (compiler.hooks && compiler.hooks[v4Name]) { compiler.hooks[v4Name].tapAsync('webpack-spritesmith', cb); } else { compiler.plugin(v3Name, cb); } } getWatcher(cb) { if (this._watcher) { cb && cb(undefined, this._watcher); } else { this._watcher = gaze(this.options.src.glob, _objectSpread({}, this.options.src.options, { cwd: this.options.src.cwd }), (err, watcher) => { watcher.on('end', () => { this._watcher = null; }); cb && cb(err, watcher); }); } return this._watcher; } apply(compiler) { this.compilerContext = compiler.options.context; this._hook(compiler, 'run', 'run', (compiler, cb) => { this.compile(() => { // without closing the gaze instance, the build will never finish this.getWatcher().close(); cb(); }); }); let watchStarted = false; this._hook(compiler, 'watch-run', 'watchRun', (watching, watchRunCallback) => { this.isInitial = !watchStarted; if (watchStarted) { return watchRunCallback(); } watchStarted = true; this.getWatcher((err, watcher) => { err && watchRunCallback(err); watcher.on('all', () => { this.compile(() => {}); }); }); return this.compile(watchRunCallback); }); this._hook(compiler, 'emit', 'emit', (compilation, cb) => { compilation.errors = compilation.errors.concat(this.metaOutput.errors.map(x => 'webpack-spritesmith: ' + x)); compilation.warnings = compilation.warnings.concat(this.metaOutput.warnings.map(x => 'webpack-spritesmith: ' + x)); cb(); }); } _compile() { var _this = this; return _asyncToGenerator(function* () { const compileStrategy = _this.useRetinaTemplate ? require('./compileRetina') : require('./compileNormal'); const sourceImagesByFolder = _this.getWatcher().watched(); const allSourceImages = Object.values(sourceImagesByFolder).reduce((allFiles, files) => [...allFiles, ...files], []).filter(x => !x.endsWith(path.sep)); const sourceImageBySpriteName = allSourceImages.reduce((sourceImageBySpriteName, sourceImage) => { const spriteName = _this.options.apiOptions.generateSpriteName(sourceImage); if (sourceImageBySpriteName[spriteName]) { if (_this.options.logCreatedFiles) { const shortOldFile = path.relative(_this.compilerContext, sourceImageBySpriteName[spriteName]); const shortReplacedFile = path.relative(_this.compilerContext, sourceImage); _this.metaOutput.warnings.push(`Sprite name collision for '${spriteName}': discarding '${shortOldFile}', using '${shortReplacedFile}'`); } } sourceImageBySpriteName[spriteName] = sourceImage; return sourceImageBySpriteName; }, {}), sourceImages = Object.values(sourceImageBySpriteName); const compiledFilesPaths = yield compileStrategy(_this.options, _this.metaOutput, _this.isInitial, sourceImages); if (!compiledFilesPaths) return; if (_this.options.logCreatedFiles) { _this.logCompiledFiles(compiledFilesPaths); } const jobs = []; if (_this.prevCompiledFilePaths) { _this.prevCompiledFilePaths.css.forEach(prevCss => { if (!compiledFilesPaths.css.includes(prevCss)) { jobs.push(fs.unlink(prevCss)); } }); _this.prevCompiledFilePaths.images.forEach(prevImgPath => { if (!compiledFilesPaths.images.includes(prevImgPath)) { jobs.push(fs.unlink(prevImgPath)); } }); } _this.prevCompiledFilePaths = compiledFilesPaths; yield Promise.all(jobs); })(); } logCompiledFiles(compiledFilesPaths) { console.log('webpack-spritesmith generated files'); console.log('images:'); console.log(compiledFilesPaths.images.map(x => ' ' + path.relative(this.compilerContext, x)).join('\n')); console.log('api:'); console.log(compiledFilesPaths.css.map(x => ' ' + path.relative(this.compilerContext, x)).join('\n')); } compile(compileCallback) { this._compile().then(compileCallback, err => { console.log(err); this.metaOutput.errors.push(err); compileCallback(); }); } };