Skip to content

Commit 54fd701

Browse files
committed
Add classGenerator option
1 parent c8bd3ac commit 54fd701

File tree

6 files changed

+63
-15
lines changed

6 files changed

+63
-15
lines changed

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,19 @@ ignorePrefixRegExp: '((hover|focus|xs|md|sm|lg|xl)[\\\\]*:)*',
7474
```
7575
In this case, `hover\:xs\:c-textbox__input` becomes `hover\:xs\:a`.
7676

77+
#### classGenerator
78+
Override the default class name generator.
79+
80+
```js
81+
// original: original class name
82+
// opts: options of the plugin
83+
// context: own context of the class generator(initial value is just an empty object)
84+
classGenerator: (original, opts, context) => {
85+
// return custom generated class name.
86+
// Or return undefined if you want to leave it to the original behavior.
87+
}
88+
```
89+
7790
### Example
7891
#### Source code
7992
```html

lib/classGenerator.js

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,15 @@ const acceptChars = 'abcdefghijklmnopqrstuvwxyz_-0123456789'.split('');
66
function ClassGenerator() {
77
this.newClassMap = {};
88
this.newClassSize = 0;
9+
this.context = {}
910
}
1011

1112
function stripEscapeSequence(words) {
1213
return words.replace(/\\/g, '');
1314
}
1415

1516
ClassGenerator.prototype = {
16-
generateClassName: function(original, opts) {
17-
original = stripEscapeSequence(original);
18-
const cn = this.newClassMap[original];
19-
if (cn) return cn;
20-
17+
defaultClassGenerator: function() {
2118
const chars = []
2219
let rest = (this.newClassSize - (this.newClassSize % acceptPrefix.length)) / acceptPrefix.length
2320
if (rest > 0) {
@@ -36,6 +33,21 @@ ClassGenerator.prototype = {
3633
let prefixIndex = this.newClassSize % acceptPrefix.length
3734

3835
const newClassName = `${acceptPrefix[prefixIndex]}${chars.join('')}`
36+
return newClassName;
37+
},
38+
generateClassName: function(original, opts) {
39+
original = stripEscapeSequence(original);
40+
const cn = this.newClassMap[original];
41+
if (cn) return cn;
42+
43+
let newClassName;
44+
if (opts.classGenerator) {
45+
newClassName = opts.classGenerator(original, opts, this.context);
46+
}
47+
if (!newClassName) {
48+
newClassName = this.defaultClassGenerator();
49+
}
50+
3951
if (opts.reserveClassName && opts.reserveClassName.includes(newClassName)) {
4052
if (opts.log) {
4153
console.log(`The class name has been reserved. ${chalk.green(newClassName)}`);

lib/optimizer.js

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ const { ReplaceSource } = require('webpack-sources');
22
const chalk = require('./chalk');
33
const ClassGenerator = require('./classGenerator');
44

5-
const classGenerator = new ClassGenerator()
6-
7-
const validate = (opts) => {
5+
const validate = (opts, classGenerator) => {
86
if (!opts.log) return;
97
for (let className in classGenerator.newClassMap) {
108
const c = classGenerator.newClassMap[className];
@@ -19,7 +17,7 @@ const validate = (opts) => {
1917
}
2018
};
2119

22-
const optimize = (chunk, compilation, opts) => chunk.files.forEach((file) => {
20+
const optimize = (chunk, compilation, opts, classGenerator) => chunk.files.forEach((file) => {
2321
let classnameRegex;
2422
if (file.match(/.+\.css.*$/)) {
2523
classnameRegex = new RegExp(`\\\.(${opts.classNameRegExp})`, 'g');
@@ -85,9 +83,9 @@ const optimize = (chunk, compilation, opts) => chunk.files.forEach((file) => {
8583

8684
const optimizer = (compiler, compilation, opts) => (chunks) => {
8785
if (!opts.classNameRegExp) throw new Error("'classNameRegExp' option is required. e.g. '[c]-[a-z][a-zA-Z0-9_]*'");
88-
89-
chunks.forEach((chunk) => optimize(chunk, compilation, opts));
90-
validate(opts);
86+
const classGenerator = new ClassGenerator();
87+
chunks.forEach((chunk) => optimize(chunk, compilation, opts, classGenerator));
88+
validate(opts, classGenerator);
9189
}
9290

9391
module.exports = optimizer;

spec/BasicSpec.js

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
var path = require('path');
22
var fs = require('fs');
3-
var webpack = require('webpack');
43
var rimraf = require('rimraf');
4+
var webpack = require('webpack');
55
var webpackMajorVersion = Number(require('webpack/package.json').version.split('.')[0]);
66
if (webpackMajorVersion < 4) {
77
var CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
@@ -197,4 +197,28 @@ describe('MangleCssClassPlugin', () => {
197197
expect(classNameWithEscape.name).toBe(classNameWithoutEscape.name);
198198
done();
199199
});
200+
201+
it('override class name generator', (done) => {
202+
testPlugin({
203+
entry: [path.join(__dirname, 'fixtures/case4.js')],
204+
output: {
205+
path: OUTPUT_DIR,
206+
filename: 'case4.js',
207+
},
208+
plugins: [new MangleCssClassPlugin({
209+
classNameRegExp: defaultCssClassRegExp,
210+
log: true,
211+
classGenerator: (original, opts, context) => {
212+
if (!context.id) {
213+
context.id = 1;
214+
}
215+
if (original.startsWith('c-')) {
216+
const className = `c${context.id}`;
217+
context.id++;
218+
return className;
219+
}
220+
}
221+
})]
222+
}, ["<p class=\\\"c1\\\">hoge-a<div class=\\\"b c1\\\">CASE 4</div></p>"], done);
223+
});
200224
});

spec/fixtures/case4.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
const a = '<p class="c-a">hoge-a<div class="l-abc c-a">CASE 4</div></p>';

spec/support/jasmine.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
"spec_files": [
44
"**/*[sS]pec.js"
55
],
6-
"stopSpecOnExpectationFailure": false,
7-
"random": true
6+
"stopSpecOnExpectationFailure": true,
7+
"random": false
88
}

0 commit comments

Comments
 (0)