Skip to content

Commit 5d382c4

Browse files
Merge pull request #2 from metalabdesign/custom-names
Support custom names for chunk and imports file.
2 parents 150cb14 + f2c9ed5 commit 5d382c4

File tree

4 files changed

+109
-15
lines changed

4 files changed

+109
-15
lines changed

README.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,21 @@ module.exports = {
4040
},
4141
plugins: [
4242
new ExtractTextPlugin('styles.css'),
43-
new CSSSplitWebpackPlugin({size: 4000, imports: true}),
43+
new CSSSplitWebpackPlugin({size: 4000}),
4444
],
4545
};
46-
4746
```
4847

48+
The following configuration options are available:
49+
50+
**size**: `default: 4000` The maximum number of CSS rules allowed in a single file. To make things work with IE this value should be somewhere around `4000`.
51+
52+
**imports**: `default: false` If you originally built your app to only ever consider using one CSS file then this flag is for you. It creates an additional CSS file that imports all of the split files. You pass `true` to turn this feature on, or a string with the name you'd like the generated file to have.
53+
54+
**filename**: `default: "[name]-[part].[ext]"` Control how the split files have their names generated. The default uses the parent's filename and extension, but adds in the part number.
55+
56+
**preserve**: `default: false`. Keep the original unsplit file as well. Sometimes this is desirable if you want to target a specific browser (IE) with the split files and then serve the unsplit ones to everyone else.
57+
4958
[webpack]: http://webpack.github.io/
5059
[herp]: https://github.com/ONE001/css-file-rules-webpack-separator
5160
[postcss]: https://github.com/postcss/postcss

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"webpack": "^1.13.0"
3737
},
3838
"dependencies": {
39+
"loader-utils": "^0.2.15",
3940
"postcss": "^5.0.19"
4041
},
4142
"peerDependencies": {

src/index.js

Lines changed: 76 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import postcss from 'postcss';
22
import chunk from './chunk';
33
import SourceMapSource from 'webpack/lib/SourceMapSource';
44
import RawSource from 'webpack/lib/RawSource';
5+
import {interpolateName} from 'loader-utils';
56

67
/**
78
* Detect if a file should be considered for CSS splitting.
@@ -17,6 +18,47 @@ const isCSS = (name : string) : boolean => /\.css$/.test(name);
1718
*/
1819
const strip = (str : string) : string => str.replace(/\/$/, '');
1920

21+
/**
22+
* Create a function that generates names based on some input. This uses
23+
* webpack's name interpolator under the hood, but since webpack's argument
24+
* list is all funny this exists just to simplify things.
25+
* @param {String} input Name to be interpolated.
26+
* @returns {Function} Function to do the interpolating.
27+
*/
28+
const nameInterpolator = (input) => ({file, content, index}) => {
29+
const res = interpolateName({
30+
context: '/',
31+
resourcePath: `/${file}`,
32+
}, input, {
33+
content,
34+
}).replace(/\[part\]/g, index + 1);
35+
return res;
36+
};
37+
38+
/**
39+
* Normalize the `imports` argument to a function.
40+
* @param {Boolean|String} input The name of the imports file, or a boolean
41+
* to use the default name.
42+
* @param {Boolean} preserve True if the default name should not clash.
43+
* @returns {Function} Name interpolator.
44+
*/
45+
const normalizeImports = (input, preserve) => {
46+
switch (typeof input) {
47+
case 'string':
48+
return nameInterpolator(input);
49+
case 'boolean':
50+
if (input) {
51+
if (preserve) {
52+
return nameInterpolator('[name]-split.[ext]');
53+
}
54+
return ({file}) => file;
55+
}
56+
return () => false;
57+
default:
58+
throw new TypeError();
59+
}
60+
};
61+
2062
/**
2163
* Webpack plugin to split CSS assets into multiple files. This is primarily
2264
* used for dealing with IE <= 9 which cannot handle more than ~4000 rules
@@ -26,10 +68,23 @@ export default class CSSSplitWebpackPlugin {
2668
/**
2769
* Create new instance of CSSSplitWebpackPlugin.
2870
* @param {Number} size Maximum number of rules for a single file.
29-
* @param {Boolean} imports True to generate an additional import asset.
71+
* @param {Boolean|String} imports Truish to generate an additional import
72+
* asset. When a boolean use the default name for the asset.
73+
* @param {String} filename Control the generated split file name.
74+
* @param {Boolean} preserve True to keep the original unsplit file.
3075
*/
31-
constructor({size = 4000, imports = false}) {
32-
this.options = {size, imports};
76+
constructor({
77+
size = 4000,
78+
imports = false,
79+
filename = '[name]-[part].[ext]',
80+
preserve,
81+
}) {
82+
this.options = {
83+
size,
84+
imports: normalizeImports(imports, preserve),
85+
filename: nameInterpolator(filename),
86+
preserve,
87+
};
3388
}
3489

3590
/**
@@ -43,10 +98,12 @@ export default class CSSSplitWebpackPlugin {
4398
const input = asset.sourceAndMap ? asset.sourceAndMap() : {
4499
source: asset.source(),
45100
};
46-
// Function for generating a new name for the split files.
47-
// TODO: Make this configurable.
48-
const name = (i) =>
49-
key.replace(/(.*)\.css(\..+)?$/, `$1-${i + 1}.css$2`);
101+
const name = (i) => this.options.filename({
102+
...asset,
103+
content: input.source,
104+
file: key,
105+
index: i,
106+
});
50107
return postcss([chunk({
51108
...this.options,
52109
result: (i) => {
@@ -107,15 +164,21 @@ export default class CSSSplitWebpackPlugin {
107164
assets[file._name] = file;
108165
chunk.files.push(file._name);
109166
});
110-
// Remove or rewrite the existing file.
111-
if (this.options.imports) {
112-
assets[entry.file] = new RawSource(entry.chunks.map((file) => {
113-
return `@import "${publicPath}/${file._name}";`;
114-
}).join('\n'));
115-
} else {
167+
const content = entry.chunks.map((file) => {
168+
return `@import "${publicPath}/${file._name}";`;
169+
}).join('\n');
170+
const imports = this.options.imports({
171+
...entry,
172+
content,
173+
});
174+
if (!this.options.preserve) {
116175
chunk.files.splice(chunk.files.indexOf(entry.file), 1);
117176
delete assets[entry.file];
118177
}
178+
if (imports) {
179+
assets[imports] = new RawSource(content);
180+
chunk.files.push(imports);
181+
}
119182
});
120183
return Promise.resolve();
121184
});

test/spec/index.spec.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,25 @@ describe('CSSSplitWebpackPlugin', () => {
6767
.to.not.contain('styles.css');
6868
});
6969
});
70+
it('should allow customization of import name', () => {
71+
return webpack({size: 3, imports: 'potato.css'}).then((stats) => {
72+
expect(stats).to.not.be.null;
73+
expect(stats.assetsByChunkName).to.have.property('main')
74+
.to.contain('potato.css');
75+
});
76+
});
77+
it('should allow preservation of the original unsplit file', () => {
78+
return webpack({size: 3, imports: false, preserve: true}).then((stats) => {
79+
expect(stats).to.not.be.null;
80+
expect(stats.assetsByChunkName).to.have.property('main')
81+
.to.contain('styles.css');
82+
});
83+
});
84+
it('should give sensible names by default', () => {
85+
return webpack({size: 3, imports: true, preserve: true}).then((stats) => {
86+
expect(stats).to.not.be.null;
87+
expect(stats.assetsByChunkName).to.have.property('main')
88+
.to.contain('styles-split.css');
89+
});
90+
});
7091
});

0 commit comments

Comments
 (0)