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
122 lines
3.4 KiB
4 years ago
|
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;
|