Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(postcss-reduce-idents): handle uppercase #705

Merged
merged 1 commit into from Jan 31, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
82 changes: 80 additions & 2 deletions packages/postcss-reduce-idents/src/__tests__/index.js
Expand Up @@ -13,33 +13,67 @@ test(
'@keyframes a{0%{color:#fff}to{color:#000}}.one{animation-name:a}'
);

test(
'should rename keyframes (uppercase)',
processCSS,
'@KEYFRAMES whiteToBlack{0%{color:#fff}to{color:#000}}.one{ANIMATION-NAME:whiteToBlack}',
'@KEYFRAMES a{0%{color:#fff}to{color:#000}}.one{ANIMATION-NAME:a}'
);

test(
'should rename multiple keyframes',
processCSS,
'@keyframes whiteToBlack{0%{color:#fff}to{color:#000}}@keyframes fadeOut{0%{opacity:1}to{opacity:0}}.one{animation-name:whiteToBlack}.two{animation-name:fadeOut}',
'@keyframes a{0%{color:#fff}to{color:#000}}@keyframes b{0%{opacity:1}to{opacity:0}}.one{animation-name:a}.two{animation-name:b}',
);

test(
'should rename multiple keyframes (uppercase)',
processCSS,
'@KEYFRAMES whiteToBlack{0%{color:#fff}to{color:#000}}@KEYFRAMES fadeOut{0%{opacity:1}to{opacity:0}}.one{animation-name:whiteToBlack}.two{animation-name:fadeOut}',
'@KEYFRAMES a{0%{color:#fff}to{color:#000}}@KEYFRAMES b{0%{opacity:1}to{opacity:0}}.one{animation-name:a}.two{animation-name:b}',
);

test(
'should reuse the same animation name for vendor prefixed keyframes',
processCSS,
'@-webkit-keyframes whiteToBlack{0%{color:#fff}to{color:#000}}@keyframes whiteToBlack{0%{color:#fff}to{color:#000}}div{-webkit-animation-name:whiteToBlack;animation-name:whiteToBlack}',
'@-webkit-keyframes a{0%{color:#fff}to{color:#000}}@keyframes a{0%{color:#fff}to{color:#000}}div{-webkit-animation-name:a;animation-name:a}'
);

test(
'should reuse the same animation name for vendor prefixed keyframes',
processCSS,
'@-WEBKIT-KEYFRAMES whiteToBlack{0%{color:#fff}to{color:#000}}@KEYFRAMES whiteToBlack{0%{color:#fff}to{color:#000}}div{-webkit-animation-name:whiteToBlack;animation-name:whiteToBlack}',
'@-WEBKIT-KEYFRAMES a{0%{color:#fff}to{color:#000}}@KEYFRAMES a{0%{color:#fff}to{color:#000}}div{-webkit-animation-name:a;animation-name:a}'
);

test(
'should support multiple animations',
processCSS,
'@keyframes one{0%{transform:rotate(0deg)}to{transform:rotate(360deg)}}@keyframes two{0%{border-width:0;opacity:0}}.loader{animation:one 1250ms infinite linear, two .3s ease-out both}',
'@keyframes a{0%{transform:rotate(0deg)}to{transform:rotate(360deg)}}@keyframes b{0%{border-width:0;opacity:0}}.loader{animation:a 1250ms infinite linear, b .3s ease-out both}'
);

test(
'should support multiple animations (uppercase)',
processCSS,
'@KEYFRAMES one{0%{transform:rotate(0deg)}to{transform:rotate(360deg)}}@KEYFRAMES two{0%{border-width:0;opacity:0}}.loader{animation:one 1250ms infinite linear, two .3s ease-out both}',
'@KEYFRAMES a{0%{transform:rotate(0deg)}to{transform:rotate(360deg)}}@KEYFRAMES b{0%{border-width:0;opacity:0}}.loader{animation:a 1250ms infinite linear, b .3s ease-out both}'
);

test(
'should not touch animation names that are not defined in the file',
passthroughCSS,
'.one{animation-name:fadeInUp}'
);

test(
'should not touch animation names that are not defined in the file (uppercase)',
passthroughCSS,
'.one{ANIMATION-NAME:fadeInUp}'
);

test(
'should not touch keyframes that are not referenced in the file',
passthroughCSS,
Expand All @@ -59,6 +93,13 @@ test(
'@counter-style a{system:extends decimal;suffix:"> "}ol{list-style:a}'
);

test(
'should rename counter styles (uppercase)',
processCSS,
'@COUNTER-STYLE custom{system:extends decimal;suffix:"> "}ol{LIST-STYLE:custom}',
'@COUNTER-STYLE a{system:extends decimal;suffix:"> "}ol{LIST-STYLE:a}'
);

test(
'should rename multiple counter styles & be aware of extensions',
processCSS,
Expand All @@ -85,6 +126,13 @@ test(
'body{counter-reset:a}h3:before{counter-increment:a;content:"Section" counter(a) ": "}'
);

test(
'should rename counters (uppercase)',
processCSS,
'body{COUNTER-RESET:section}h3:before{COUNTER-INCREMENT:section;CONTENT:"Section" counter(section) ": "}',
'body{COUNTER-RESET:a}h3:before{COUNTER-INCREMENT:a;CONTENT:"Section" counter(a) ": "}'
);

test(
'should rename counters (2)',
processCSS,
Expand All @@ -99,6 +147,13 @@ test(
'li{counter-increment:a}li::marker{content:"(" counters(a,".") ")"}'
);

test(
'should rename counters (3) (uppercase)',
processCSS,
'li{counter-increment:list-item}li::marker{content:"(" COUNTERS(list-item,".") ")"}',
'li{counter-increment:a}li::marker{content:"(" COUNTERS(a,".") ")"}'
);

test(
'should rename multiple counters',
processCSS,
Expand Down Expand Up @@ -192,6 +247,25 @@ test(
].join('')
);

test(
'should rename grid-template-areas and grid-area (uppercase)',
processCSS,
[
'body{GRID-TEMPLATE-AREAS:"head head" \n"nav main"\n"nav foot";}',
'header { GRID-AREA: head }',
'nav{GRID-AREA:nav}',
'main{GRID-AREA:main}',
'footer{GRID-AREA:foot}',
].join(''),
[
'body{GRID-TEMPLATE-AREAS:"a a" "b c" "b d";}',
'header { GRID-AREA: a }',
'nav{GRID-AREA:b}',
'main{GRID-AREA:c}',
'footer{GRID-AREA:d}',
].join('')
);

test(
'should rename grid-template short syntax',
processCSS,
Expand Down Expand Up @@ -254,10 +328,14 @@ test('should not generate same ident when plugin instance is reused', t => {
const instance = postcss(plugin);
return Promise.all([
instance.process('@keyframes whiteToBlack{0%{color:#fff}to{color:#000}}.one{animation-name:whiteToBlack}'),
instance.process('@KEYFRAMES whiteToBlack{0%{color:#fff}to{color:#000}}.one{animation-name:whiteToBlack}'),
instance.process('@keyframes fadeOut{0%{opacity:1}to{opacity:0}}.two{animation-name:fadeOut}'),
]).then(([result1, result2]) => {
instance.process('@KEYFRAMES fadeOut{0%{opacity:1}to{opacity:0}}.two{animation-name:fadeOut}'),
]).then(([result1, result2, result3, result4]) => {
t.is(result1.css, '@keyframes a{0%{color:#fff}to{color:#000}}.one{animation-name:a}');
t.is(result2.css, '@keyframes b{0%{opacity:1}to{opacity:0}}.two{animation-name:b}');
t.is(result2.css, '@KEYFRAMES a{0%{color:#fff}to{color:#000}}.one{animation-name:a}');
t.is(result3.css, '@keyframes b{0%{opacity:1}to{opacity:0}}.two{animation-name:b}');
t.is(result4.css, '@KEYFRAMES b{0%{opacity:1}to{opacity:0}}.two{animation-name:b}');
});
});

Expand Down
1 change: 1 addition & 0 deletions packages/postcss-reduce-idents/src/index.js
Expand Up @@ -14,6 +14,7 @@ export default postcss.plugin('postcss-reduce-idents', ({
encoder = encode,
} = {}) => {
const reducers = [];

counter && reducers.push(counterReducer());
counterStyle && reducers.push(counterStyleReducer());
keyframes && reducers.push(keyframesReducer());
Expand Down
1 change: 1 addition & 0 deletions packages/postcss-reduce-idents/src/lib/cache.js
Expand Up @@ -2,6 +2,7 @@ export default function (value, encoder, cache) {
if (cache[value]) {
return;
}

cache[value] = {
ident: encoder(value, Object.keys(cache).length),
count: 0,
Expand Down
10 changes: 7 additions & 3 deletions packages/postcss-reduce-idents/src/lib/counter-style.js
Expand Up @@ -25,14 +25,15 @@ export default function () {

if (
type === 'atrule' &&
/counter-style/.test(name) &&
RESERVED_KEYWORDS.indexOf(node.params) === -1
/counter-style/i.test(name) &&
RESERVED_KEYWORDS.indexOf(node.params.toLowerCase()) === -1
) {
addToCache(node.params, encoder, cache);

atRules.push(node);
}

if (type === 'decl' && /(list-style|system)/.test(prop)) {
if (type === 'decl' && /(list-style|system)/i.test(prop)) {
decls.push(node);
}
},
Expand All @@ -43,13 +44,16 @@ export default function () {
decl.value = valueParser(decl.value).walk(node => {
if (node.type === 'word' && node.value in cache) {
cache[node.value].count++;

node.value = cache[node.value].ident;
}
}).toString();
});

// Iterate each at rule and change their name if references to them have been found
atRules.forEach(rule => {
const cached = cache[rule.params];

if (cached && cached.count > 0) {
rule.params = cached.ident;
}
Expand Down
18 changes: 14 additions & 4 deletions packages/postcss-reduce-idents/src/lib/counter.js
Expand Up @@ -19,46 +19,56 @@ export default function () {
return;
}

if (/counter-(reset|increment)/.test(prop)) {
if (/counter-(reset|increment)/i.test(prop)) {
node.value = valueParser(node.value).walk(child => {
if (
child.type === 'word' &&
!isNum(child) &&
RESERVED_KEYWORDS.indexOf(child.value) === -1
RESERVED_KEYWORDS.indexOf(child.value.toLowerCase()) === -1
) {
addToCache(child.value, encoder, cache);

child.value = cache[child.value].ident;
}
});

declOneCache.push(node);
} else if (/content/.test(prop)) {
} else if (/content/i.test(prop)) {
declTwoCache.push(node);
}
},

transform () {
declTwoCache.forEach(decl => {
decl.value = valueParser(decl.value).walk(node => {
const {type, value} = node;
const {type} = node;

const value = node.value.toLowerCase();

if (type === 'function' && (value === 'counter' || value === 'counters')) {
walk(node.nodes, child => {
if (child.type === 'word' && child.value in cache) {
cache[child.value].count++;

child.value = cache[child.value].ident;
}
});
}

if (type === 'space') {
node.value = ' ';
}

return false;
}).toString();
});

declOneCache.forEach(decl => {
decl.value = decl.value.walk(node => {
if (node.type === 'word' && !isNum(node)) {
Object.keys(cache).forEach(key => {
const cached = cache[key];

if (cached.ident === node.value && !cached.count) {
node.value = key;
}
Expand Down
2 changes: 2 additions & 0 deletions packages/postcss-reduce-idents/src/lib/encode.js
Expand Up @@ -4,6 +4,7 @@ export default function encode (val, num) {
let character = num % base;
let result = characters[character];
let remainder = Math.floor(num / base);

if (remainder) {
base = 64;
characters = characters + '0123456789-_';
Expand All @@ -13,5 +14,6 @@ export default function encode (val, num) {
result = result + characters[character];
}
}

return result;
};
14 changes: 9 additions & 5 deletions packages/postcss-reduce-idents/src/lib/grid-template.js
Expand Up @@ -16,45 +16,49 @@ export default function () {
return;
}

if (/(grid-template|grid-template-areas)/.test(node.prop)) {
if (/(grid-template|grid-template-areas)/i.test(node.prop)) {
valueParser(node.value).walk(child => {
if (child.type === 'string') {
child.value.split(/\s+/).forEach(word => {
if (/\.+/.test(word)) { // reduce empty zones to a single `.`
node.value = node.value.replace(word, ".");
} else if (word && RESERVED_KEYWORDS.indexOf(word) === -1) {
} else if (word && RESERVED_KEYWORDS.indexOf(word.toLowerCase()) === -1) {
addToCache(word, encoder, cache);
}
});
}
});

declCache.push(node);
} else if (node.prop === 'grid-area') {
} else if (node.prop.toLowerCase() === 'grid-area') {
valueParser(node.value).walk(child => {
if (child.type === 'word' && RESERVED_KEYWORDS.indexOf(child.value) === -1) {
addToCache(child.value, encoder, cache);
}
});

declCache.push(node);
}
},

transform () {
declCache.forEach(decl => {
decl.value = valueParser(decl.value).walk(node => {
if (/(grid-template|grid-template-areas)/.test(decl.prop)) {
if (/(grid-template|grid-template-areas)/i.test(decl.prop)) {
node.value.split(/\s+/).forEach(word => {
if (word in cache) {
node.value = node.value.replace(word, cache[word].ident);
}
});
node.value = node.value.replace(/\s+/g, " "); // merge white-spaces
}
if (decl.prop === 'grid-area' && !isNum(node)) {

if (decl.prop.toLowerCase() === 'grid-area' && !isNum(node)) {
if (node.value in cache) {
node.value = cache[node.value].ident;
}
}

return false;
}).toString();
});
Expand Down
9 changes: 6 additions & 3 deletions packages/postcss-reduce-idents/src/lib/keyframes.js
Expand Up @@ -16,27 +16,29 @@ export default function () {

if (
type === 'atrule' &&
/keyframes/.test(name) &&
RESERVED_KEYWORDS.indexOf(node.params) === -1
/keyframes/i.test(name) &&
RESERVED_KEYWORDS.indexOf(node.params.toLowerCase()) === -1
) {
addToCache(node.params, encoder, cache);
atRules.push(node);
}

if (type === 'decl' && /animation/.test(prop)) {
if (type === 'decl' && /animation/i.test(prop)) {
decls.push(node);
}
},

transform () {
let referenced = [];

// Iterate each property and change their names
decls.forEach(decl => {
decl.value = valueParser(decl.value).walk(node => {
if (node.type === 'word' && node.value in cache) {
if (!~referenced.indexOf(node.value)) {
referenced.push(node.value);
}

cache[node.value].count++;
node.value = cache[node.value].ident;
}
Expand All @@ -46,6 +48,7 @@ export default function () {
// Iterate each at rule and change their name if references to them have been found
atRules.forEach(rule => {
const cached = cache[rule.params];

if (cached && cached.count > 0 && !!~referenced.indexOf(rule.params)) {
rule.params = cached.ident;
}
Expand Down