UNPKG

css-split-webpack-plugin

Version:

Split your CSS for stupid browsers using [webpack] and [postcss].

268 lines (237 loc) 9.48 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _postcss = require('postcss'); var _postcss2 = _interopRequireDefault(_postcss); var _chunk = require('./chunk'); var _chunk2 = _interopRequireDefault(_chunk); var _webpackSources = require('webpack-sources'); var _loaderUtils = require('loader-utils'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * Detect if a file should be considered for CSS splitting. * @param {String} name Name of the file. * @returns {Boolean} True if to consider the file, false otherwise. */ var isCSS = function isCSS(name) { return (/\.css$/.test(name) ); }; /** * Remove the trailing `/` from URLs. * @param {String} str The url to strip the trailing slash from. * @returns {String} The stripped url. */ var strip = function strip(str) { return str.replace(/\/$/, ''); }; /** * Create a function that generates names based on some input. This uses * webpack's name interpolator under the hood, but since webpack's argument * list is all funny this exists just to simplify things. * @param {String} input Name to be interpolated. * @returns {Function} Function to do the interpolating. */ var nameInterpolator = function nameInterpolator(input) { return function (_ref) { var file = _ref.file, content = _ref.content, index = _ref.index; var res = (0, _loaderUtils.interpolateName)({ context: '/', resourcePath: `/${file}` }, input, { content }).replace(/\[part\]/g, index + 1); return res; }; }; /** * Normalize the `imports` argument to a function. * @param {Boolean|String} input The name of the imports file, or a boolean * to use the default name. * @param {Boolean} preserve True if the default name should not clash. * @returns {Function} Name interpolator. */ var normalizeImports = function normalizeImports(input, preserve) { switch (typeof input) { case 'string': return nameInterpolator(input); case 'boolean': if (input) { if (preserve) { return nameInterpolator('[name]-split.[ext]'); } return function (_ref2) { var file = _ref2.file; return file; }; } return function () { return false; }; default: throw new TypeError(); } }; /** * Webpack plugin to split CSS assets into multiple files. This is primarily * used for dealing with IE <= 9 which cannot handle more than ~4000 rules * in a single stylesheet. */ var CSSSplitWebpackPlugin = function () { /** * Create new instance of CSSSplitWebpackPlugin. * @param {Number} size Maximum number of rules for a single file. * @param {Boolean|String} imports Truish to generate an additional import * asset. When a boolean use the default name for the asset. * @param {String} filename Control the generated split file name. * @param {Boolean} defer Defer splitting until the `emit` phase. Normally * only needed if something else in your pipeline is mangling things at * the emit phase too. * @param {Boolean} preserve True to keep the original unsplit file. */ function CSSSplitWebpackPlugin(_ref3) { var _ref3$size = _ref3.size, size = _ref3$size === undefined ? 4000 : _ref3$size, _ref3$imports = _ref3.imports, imports = _ref3$imports === undefined ? false : _ref3$imports, _ref3$filename = _ref3.filename, filename = _ref3$filename === undefined ? '[name]-[part].[ext]' : _ref3$filename, preserve = _ref3.preserve, _ref3$defer = _ref3.defer, defer = _ref3$defer === undefined ? false : _ref3$defer; _classCallCheck(this, CSSSplitWebpackPlugin); this.options = { size, imports: normalizeImports(imports, preserve), filename: nameInterpolator(filename), preserve, defer }; } /** * Generate the split chunks for a given CSS file. * @param {String} key Name of the file. * @param {Object} asset Valid webpack Source object. * @returns {Promise} Promise generating array of new files. */ _createClass(CSSSplitWebpackPlugin, [{ key: 'file', value: function file(key, asset) { var _this = this; // Use source-maps when possible. var input = asset.sourceAndMap ? asset.sourceAndMap() : { source: asset.source() }; var getName = function getName(i) { return _this.options.filename(_extends({}, asset, { content: input.source, file: key, index: i })); }; return (0, _postcss2.default)([(0, _chunk2.default)(this.options)]).process(input.source, { from: undefined, map: { prev: input.map } }).then(function (result) { return Promise.resolve({ file: key, chunks: result.chunks.map(function (_ref4, i) { var css = _ref4.css, map = _ref4.map; var name = getName(i); var result = map ? new _webpackSources.SourceMapSource(css, name, map.toString()) : new _webpackSources.RawSource(css); result.name = name; return result; }) }); }); } }, { key: 'chunksMapping', value: function chunksMapping(compilation, chunks, done) { var _this2 = this; var assets = compilation.assets; var publicPath = strip(compilation.options.output.publicPath || './'); var promises = chunks.map(function (chunk) { var input = chunk.files.filter(isCSS); var items = input.map(function (name) { return _this2.file(name, assets[name]); }); return Promise.all(items).then(function (entries) { entries.forEach(function (entry) { // Skip the splitting operation for files that result in no // split occuring. if (entry.chunks.length === 1) { return; } // Inject the new files into the chunk. entry.chunks.forEach(function (file) { assets[file.name] = file; chunk.files.push(file.name); }); var content = entry.chunks.map(function (file) { return `@import "${publicPath}/${file._name}";`; }).join('\n'); var imports = _this2.options.imports(_extends({}, entry, { content })); if (!_this2.options.preserve) { chunk.files.splice(chunk.files.indexOf(entry.file), 1); delete assets[entry.file]; } if (imports) { assets[imports] = new _webpackSources.RawSource(content); chunk.files.push(imports); } }); return Promise.resolve(); }); }); Promise.all(promises).then(function () { done(); }, done); } /** * Run the plugin against a webpack compiler instance. Roughly it walks all * the chunks searching for CSS files and when it finds one that needs to be * split it does so and replaces the original file in the chunk with the split * ones. If the `imports` option is specified the original file is replaced * with an empty CSS file importing the split files, otherwise the original * file is removed entirely. * @param {Object} compiler Compiler instance * @returns {void} */ }, { key: 'apply', value: function apply(compiler) { var _this3 = this; if (this.options.defer) { // Run on `emit` when user specifies the compiler phase // Due to the incorrect css split + optimization behavior // Expected: css split should happen after optimization compiler.plugin('emit', function (compilation, done) { return _this3.chunksMapping(compilation, compilation.chunks, done); }); } else { // Only run on `this-compilation` to avoid injecting the plugin into // sub-compilers as happens when using the `extract-text-webpack-plugin`. compiler.plugin('this-compilation', function (compilation) { compilation.plugin('optimize-chunk-assets', function (chunks, done) { return _this3.chunksMapping(compilation, chunks, done); }); }); } } }]); return CSSSplitWebpackPlugin; }(); exports.default = CSSSplitWebpackPlugin; //# sourceMappingURL=index.js.map