forked from webpack-contrib/css-loader
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: resolve issue webpack-contrib#1028: optimising naming for best …
…compression
- Loading branch information
Showing
4 changed files
with
220 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
import importParser from './postcss-import-parser'; | ||
import icssParser from './postcss-icss-parser'; | ||
import OneLetterCss from './one-letter-css'; | ||
import urlParser from './postcss-url-parser'; | ||
|
||
export { importParser, icssParser, urlParser }; | ||
export { importParser, icssParser, OneLetterCss, urlParser }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/** | ||
* @author denisx <[email protected]> | ||
*/ | ||
|
||
import { interpolateName } from 'loader-utils'; | ||
|
||
export default class OneLetterCss { | ||
constructor() { | ||
// Save char symbol start positions | ||
this.a = 'a'.charCodeAt(0); | ||
this.A = 'A'.charCodeAt(0); | ||
// file hashes cache | ||
this.files = {}; | ||
/** encoding [a-zA-Z] */ | ||
this.symbols = 52; | ||
/** a half of encoding */ | ||
this.half = 26; | ||
/** prevent loop-hell */ | ||
this.maxLoop = 10; | ||
} | ||
|
||
/** encoding by rule count at file, 0 - a, 1 - b, 51 - Z, 52 - ba, etc */ | ||
getName(lastUsed) { | ||
const { a, A, symbols, maxLoop, half } = this; | ||
let name = ''; | ||
let loop = 0; | ||
let main = lastUsed; | ||
let tail = 0; | ||
|
||
while ( | ||
((main > 0 && tail >= 0) || | ||
// first step anyway needed | ||
loop === 0) && | ||
loop < maxLoop | ||
) { | ||
const newMain = Math.floor(main / symbols); | ||
|
||
tail = main % symbols; | ||
name = String.fromCharCode((tail >= half ? A - half : a) + tail) + name; | ||
main = newMain; | ||
loop += 1; | ||
} | ||
|
||
return name; | ||
} | ||
|
||
getLocalIdent(context, localIdentName, localName) { | ||
const { resourcePath } = context; | ||
const { files } = this; | ||
|
||
// check file data at cache by absolute path | ||
let fileShort = files[resourcePath]; | ||
|
||
// no file data, lets generate and save | ||
if (!fileShort) { | ||
// if we know file position, we must use base52 encoding with '_' | ||
// between rule position and file position | ||
// to avoid collapse hash combination. a_ab vs aa_b | ||
const fileShortName = interpolateName(context, '[hash:base64:8]', { | ||
content: resourcePath, | ||
}); | ||
|
||
fileShort = { name: fileShortName, lastUsed: -1, ruleNames: {} }; | ||
files[resourcePath] = fileShort; | ||
} | ||
|
||
// Get generative rule name from this file | ||
let newRuleName = fileShort.ruleNames[localName]; | ||
|
||
// If no rule - renerate new, and save | ||
if (!newRuleName) { | ||
// Count +1 | ||
fileShort.lastUsed += 1; | ||
|
||
// Generate new rule name | ||
newRuleName = this.getName(fileShort.lastUsed) + fileShort.name; | ||
|
||
// Saved | ||
fileShort.ruleNames[localName] = newRuleName; | ||
} | ||
|
||
// If has "local" at webpack settings | ||
const hasLocal = /\[local]/.test(localIdentName); | ||
|
||
// If has - add prefix | ||
return hasLocal ? `${localName}__${newRuleName}` : newRuleName; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import { OneLetterCss } from '../src/plugins'; | ||
|
||
/* webpack set */ | ||
const workSets = [ | ||
{ | ||
in: [ | ||
{ | ||
resourcePath: './file1.css', | ||
}, | ||
'[hash:base64:8]', | ||
'theme-white', | ||
], | ||
out: ['a2zADNwsK'], | ||
}, | ||
{ | ||
in: [ | ||
{ | ||
resourcePath: './file1.css', | ||
}, | ||
'[hash:base64:8]', | ||
'theme-blue', | ||
], | ||
out: ['b2zADNwsK'], | ||
}, | ||
{ | ||
in: [ | ||
{ | ||
resourcePath: './file2.css', | ||
}, | ||
'[hash:base64:8]', | ||
'text-white', | ||
], | ||
out: ['a2jlx459O'], | ||
}, | ||
{ | ||
in: [ | ||
{ | ||
resourcePath: './file2.css', | ||
}, | ||
'[hash:base64:8]', | ||
'text-blue', | ||
], | ||
out: ['b2jlx459O'], | ||
}, | ||
// for develop case | ||
{ | ||
in: [ | ||
{ | ||
resourcePath: './file2.css', | ||
}, | ||
'[local]__[hash:base64:8]', | ||
'text-blue', | ||
], | ||
out: ['text-blue__b2jlx459O'], | ||
}, | ||
]; | ||
|
||
/* encoding test set */ | ||
const encodingSets = [ | ||
{ | ||
in: [0], | ||
out: ['a'], | ||
}, | ||
{ | ||
in: [1], | ||
out: ['b'], | ||
}, | ||
{ | ||
in: [51], | ||
out: ['Z'], | ||
}, | ||
{ | ||
in: [52], | ||
out: ['ba'], | ||
}, | ||
{ | ||
in: [53], | ||
out: ['bb'], | ||
}, | ||
]; | ||
|
||
const MyOneLetterCss = new OneLetterCss(); | ||
|
||
describe('testing work case', () => { | ||
workSets.forEach((set) => { | ||
it(`should check name generate`, () => { | ||
expect(MyOneLetterCss.getLocalIdent(...set.in)).toEqual(...set.out); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('testing encoding method', () => { | ||
encodingSets.forEach((set) => { | ||
it(`should check name generate`, () => { | ||
expect(MyOneLetterCss.getName(...set.in)).toEqual(...set.out); | ||
}); | ||
}); | ||
}); |