Skip to content

Commit b4ab1c7

Browse files
committed
Merge branch 'master' of https://github.com/jacobp100/postcss-modules-local-by-default into jacobp100-master
2 parents 7ba3c64 + 285e664 commit b4ab1c7

File tree

2 files changed

+117
-4
lines changed

2 files changed

+117
-4
lines changed

index.js

Lines changed: 92 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,21 +182,109 @@ function localizeDeclValue(valueNode, context) {
182182
return newValueNode;
183183
}
184184

185-
function localizeDecl(decl, context) {
186-
var valuesNode = Tokenizer.parseValues(decl.value);
187-
var localizeName = /animation(-name)?$/.test(decl.prop);
185+
function localizeAnimationShorthandDeclValueNodes(nodes, context) {
186+
var validIdent = validIdent = /^-?[_a-z][_a-z0-9-]*$/i;
187+
188+
/*
189+
The spec defines some keywords that you can use to describe properties such as the timing
190+
function. These are still valid animation names, so as long as there is a property that accepts
191+
a keyword, it is given priority. Only when all the properties that can take a keyword are
192+
exhausted can the animation name be set to the keyword. I.e.
193+
194+
animation: infinite infinite;
195+
196+
The animation will repeat an infinite number of times from the first argument, and will have an
197+
animation name of infinite from the second.
198+
*/
199+
var animationKeywords = {
200+
'alternate': 1,
201+
'alternate-reverse': 1,
202+
'backwards': 1,
203+
'both': 1,
204+
'ease': 1,
205+
'ease-in': 1,
206+
'ease-in-out': 1,
207+
'ease-out': 1,
208+
'forwards': 1,
209+
'infinite': 1,
210+
'linear': 1,
211+
'none': Infinity, // No matter how many times you write none, it will never be an animation name
212+
'normal': 1,
213+
'paused': 1,
214+
'reverse': 1,
215+
'running': 1,
216+
'step-end': 1,
217+
'step-start': 1,
218+
'initial': Infinity,
219+
'inherit': Infinity,
220+
'unset': Infinity,
221+
};
222+
223+
var didParseAnimationName = false;
224+
var parsedAnimationKeywords = {};
225+
return nodes.map(function(valueNode) {
226+
var value = valueNode.type === 'item'
227+
? valueNode.name.toLowerCase()
228+
: null;
229+
230+
var shouldParseAnimationName = false;
231+
232+
if (!didParseAnimationName && value && validIdent.test(value)) {
233+
if (value in animationKeywords) {
234+
parsedAnimationKeywords[value] = (value in parsedAnimationKeywords)
235+
? (parsedAnimationKeywords[value] + 1)
236+
: 0;
237+
238+
shouldParseAnimationName = (parsedAnimationKeywords[value] >= animationKeywords[value]);
239+
} else {
240+
shouldParseAnimationName = true;
241+
}
242+
}
243+
244+
var subContext = {
245+
options: context.options,
246+
global: context.global,
247+
localizeNextItem: shouldParseAnimationName && !context.global
248+
};
249+
return localizeDeclNode(valueNode, subContext);
250+
});
251+
}
252+
253+
function localizeAnimationShorthandDeclValues(valuesNode, decl, context) {
254+
var newValuesNode = Object.create(valuesNode);
255+
newValuesNode.nodes = valuesNode.nodes.map(function(valueNode, index) {
256+
var newValueNode = Object.create(valueNode);
257+
newValueNode.nodes = localizeAnimationShorthandDeclValueNodes(valueNode.nodes, context);
258+
return newValueNode;
259+
});
260+
decl.value = Tokenizer.stringifyValues(newValuesNode);
261+
}
262+
263+
function localizeDeclValues(localize, valuesNode, decl, context) {
188264
var newValuesNode = Object.create(valuesNode);
189265
newValuesNode.nodes = valuesNode.nodes.map(function(valueNode) {
190266
var subContext = {
191267
options: context.options,
192268
global: context.global,
193-
localizeNextItem: localizeName && !context.global
269+
localizeNextItem: localize && !context.global
194270
};
195271
return localizeDeclValue(valueNode, subContext);
196272
});
197273
decl.value = Tokenizer.stringifyValues(newValuesNode);
198274
}
199275

276+
function localizeDecl(decl, context) {
277+
var valuesNode = Tokenizer.parseValues(decl.value);
278+
279+
var isAnimation = /animation?$/.test(decl.prop);
280+
if (isAnimation) return localizeAnimationShorthandDeclValues(valuesNode, decl, context);
281+
282+
var isAnimationName = /animation(-name)?$/.test(decl.prop);
283+
if (isAnimationName) return localizeDeclValues(true, valuesNode, decl, context);
284+
285+
return localizeDeclValues(false, valuesNode, decl, context);
286+
}
287+
200288
module.exports = postcss.plugin('postcss-modules-local-by-default', function (options) {
201289
if (typeof options !== 'object') {
202290
options = {}; // If options is undefined or not an object the plugin fails

test.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,31 @@ var tests = [
174174
input: '.foo { animation: foo, bar 5s linear 2s infinite alternate, barfoo 1s; }',
175175
expected: ':local(.foo) { animation: :local(foo), :local(bar) 5s linear 2s infinite alternate, :local(barfoo) 1s; }'
176176
},
177+
{
178+
should: 'handle animations where the first value is not the animation name',
179+
input: '.foo { animation: 1s foo; }',
180+
expected: ':local(.foo) { animation: 1s :local(foo); }'
181+
},
182+
{
183+
should: 'handle animations where the first value is not the animation name whilst also using keywords',
184+
input: '.foo { animation: 1s normal ease-out infinite foo; }',
185+
expected: ':local(.foo) { animation: 1s normal ease-out infinite :local(foo); }'
186+
},
187+
{
188+
should: 'handle animations with custom timing functions',
189+
input: '.foo { animation: 1s normal cubic-bezier(0.25, 0.5, 0.5. 0.75) foo; }',
190+
expected: ':local(.foo) { animation: 1s normal cubic-bezier(0.25, 0.5, 0.5. 0.75) :local(foo); }'
191+
},
192+
{
193+
should: 'handle animations whose names are keywords',
194+
input: '.foo { animation: 1s infinite infinite; }',
195+
expected: ':local(.foo) { animation: 1s infinite :local(infinite); }'
196+
},
197+
{
198+
should: 'handle not localize an animation shorthand value of "inherit"',
199+
input: '.foo { animation: inherit; }',
200+
expected: ':local(.foo) { animation: inherit; }'
201+
},
177202
{
178203
should: 'default to global when mode provided',
179204
input: '.foo {}',

0 commit comments

Comments
 (0)