You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

122 lines
3.4 KiB

const cssnano = require('cssnano');
const postcss = require('postcss');
/**
* Optimize cssnano plugin
*
* @param {Object} options
*/
function OptimizeCssnanoPlugin(options) {
this.options = Object.assign({
sourceMap: false,
cssnanoOptions: {
preset: 'default',
},
}, options);
if (this.options.sourceMap) {
this.options.sourceMap = Object.assign(
{inline: false},
this.options.sourceMap || {});
}
}
OptimizeCssnanoPlugin.prototype.apply = function(compiler) {
const self = this;
compiler.hooks.emit.tapAsync('OptimizeCssnanoPlugin',
function(compilation, callback) {
// Search for CSS assets
const assetsNames = Object.keys(compilation.assets)
.filter((assetName) => {
return /\.css$/i.test(assetName);
});
let hasErrors = false;
const promises = [];
// Generate promises for each minification
assetsNames.forEach((assetName) => {
// Original CSS
const asset = compilation.assets[assetName];
const originalCss = asset.source();
// Options for particalar cssnano call
const postCssOptions = {
from: assetName,
to: assetName,
map: false,
};
const cssnanoOptions = self.options.cssnanoOptions;
// Extract or remove previous map
const mapName = assetName + '.map';
if (self.options.sourceMap) {
// Use previous map if exist...
if (compilation.assets[mapName]) {
const mapObject = JSON.parse(compilation.assets[mapName].source());
// ... and not empty
if (mapObject.sources.length > 0 || mapObject.mappings.length > 0) {
postCssOptions.map = Object.assign({
prev: compilation.assets[mapName].source(),
}, self.options.sourceMap);
} else {
postCssOptions.map = Object.assign({}, self.options.sourceMap);
}
}
} else {
delete compilation.assets[mapName];
}
// Run minification
const promise = postcss([cssnano(cssnanoOptions)])
.process(originalCss, postCssOptions)
.then((result) => {
if (hasErrors) {
return;
}
// Extract CSS back to assets
const processedCss = result.css;
compilation.assets[assetName] = {
source: function() {
return processedCss;
},
size: function() {
return processedCss.length;
},
};
// Extract map back to assets
if (result.map) {
const processedMap = result.map.toString();
compilation.assets[mapName] = {
source: function() {
return processedMap;
},
size: function() {
return processedMap.length;
},
};
}
}
).catch(function(err) {
hasErrors = true;
throw new Error('CSS minification error: ' + err.message +
'. File: ' + assetName);
}
);
promises.push(promise);
});
Promise.all(promises)
.then(function() {
callback();
})
.catch(callback);
});
};
module.exports = OptimizeCssnanoPlugin;