Skip to content

Commit 8f63f03

Browse files
authored
Merge pull request #114 from css-modules/remove-keyframes
Remove keyframes in favor of postcss-icss-keyframes
2 parents 4fd6303 + dd05ed0 commit 8f63f03

File tree

4 files changed

+444
-290
lines changed

4 files changed

+444
-290
lines changed

package.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,21 @@
2020
},
2121
"lint-staged": {
2222
"*.js": [
23+
"eslint",
2324
"prettier --single-quote --no-semi --write",
2425
"git add"
2526
]
2627
},
28+
"eslintConfig": {
29+
"parserOptions": {
30+
"ecmaVersion": 6,
31+
"sourceType": "module"
32+
},
33+
"env": {
34+
"es6": true
35+
},
36+
"extends": "eslint:recommended"
37+
},
2738
"babel": {
2839
"presets": [
2940
[
@@ -52,6 +63,7 @@
5263
"babel-preset-env": "^1.5.1",
5364
"codecov.io": "^0.1.2",
5465
"coveralls": "^2.11.2",
66+
"eslint": "^3.19.0",
5567
"husky": "^0.13.3",
5668
"jest": "^20.0.3",
5769
"lint-staged": "^3.4.2",

src/index.js

Lines changed: 4 additions & 175 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
var postcss = require('postcss')
2-
var Tokenizer = require('css-selector-tokenizer')
1+
/* eslint-env node */
2+
import postcss from 'postcss'
3+
import Tokenizer from 'css-selector-tokenizer'
34

45
function normalizeNodeArray(nodes) {
56
var array = []
@@ -158,148 +159,9 @@ function localizeNode(node, context) {
158159
return node
159160
}
160161

161-
function localizeDeclNode(node, context) {
162-
var newNode
163-
switch (node.type) {
164-
case 'item':
165-
if (context.localizeNextItem) {
166-
newNode = Object.create(node)
167-
newNode.name = ':local(' + newNode.name + ')'
168-
context.localizeNextItem = false
169-
return newNode
170-
}
171-
break
172-
173-
case 'nested-item':
174-
var newNodes = node.nodes.map(function(n) {
175-
return localizeDeclValue(n, context)
176-
})
177-
node = Object.create(node)
178-
node.nodes = newNodes
179-
break
180-
}
181-
return node
182-
}
183-
184-
function localizeDeclValue(valueNode, context) {
185-
var newValueNode = Object.create(valueNode)
186-
newValueNode.nodes = valueNode.nodes.map(function(node) {
187-
return localizeDeclNode(node, context)
188-
})
189-
return newValueNode
190-
}
191-
192-
function localizeAnimationShorthandDeclValueNodes(nodes, context) {
193-
var validIdent = (validIdent = /^-?[_a-z][_a-z0-9-]*$/i)
194-
195-
/*
196-
The spec defines some keywords that you can use to describe properties such as the timing
197-
function. These are still valid animation names, so as long as there is a property that accepts
198-
a keyword, it is given priority. Only when all the properties that can take a keyword are
199-
exhausted can the animation name be set to the keyword. I.e.
200-
201-
animation: infinite infinite;
202-
203-
The animation will repeat an infinite number of times from the first argument, and will have an
204-
animation name of infinite from the second.
205-
*/
206-
var animationKeywords = {
207-
$alternate: 1,
208-
'$alternate-reverse': 1,
209-
$backwards: 1,
210-
$both: 1,
211-
$ease: 1,
212-
'$ease-in': 1,
213-
'$ease-in-out': 1,
214-
'$ease-out': 1,
215-
$forwards: 1,
216-
$infinite: 1,
217-
$linear: 1,
218-
$none: Infinity, // No matter how many times you write none, it will never be an animation name
219-
$normal: 1,
220-
$paused: 1,
221-
$reverse: 1,
222-
$running: 1,
223-
'$step-end': 1,
224-
'$step-start': 1,
225-
$initial: Infinity,
226-
$inherit: Infinity,
227-
$unset: Infinity
228-
}
229-
230-
var didParseAnimationName = false
231-
var parsedAnimationKeywords = {}
232-
return nodes.map(function(valueNode) {
233-
var value = valueNode.type === 'item' ? valueNode.name.toLowerCase() : null
234-
235-
var shouldParseAnimationName = false
236-
237-
if (!didParseAnimationName && value && validIdent.test(value)) {
238-
if ('$' + value in animationKeywords) {
239-
parsedAnimationKeywords['$' + value] = '$' + value in
240-
parsedAnimationKeywords
241-
? parsedAnimationKeywords['$' + value] + 1
242-
: 0
243-
244-
shouldParseAnimationName =
245-
parsedAnimationKeywords['$' + value] >= animationKeywords['$' + value]
246-
} else {
247-
shouldParseAnimationName = true
248-
}
249-
}
250-
251-
var subContext = {
252-
options: context.options,
253-
global: context.global,
254-
localizeNextItem: shouldParseAnimationName && !context.global
255-
}
256-
return localizeDeclNode(valueNode, subContext)
257-
})
258-
}
259-
260-
function localizeAnimationShorthandDeclValues(valuesNode, decl, context) {
261-
var newValuesNode = Object.create(valuesNode)
262-
newValuesNode.nodes = valuesNode.nodes.map(function(valueNode, index) {
263-
var newValueNode = Object.create(valueNode)
264-
newValueNode.nodes = localizeAnimationShorthandDeclValueNodes(
265-
valueNode.nodes,
266-
context
267-
)
268-
return newValueNode
269-
})
270-
decl.value = Tokenizer.stringifyValues(newValuesNode)
271-
}
272-
273-
function localizeDeclValues(localize, valuesNode, decl, context) {
274-
var newValuesNode = Object.create(valuesNode)
275-
newValuesNode.nodes = valuesNode.nodes.map(function(valueNode) {
276-
var subContext = {
277-
options: context.options,
278-
global: context.global,
279-
localizeNextItem: localize && !context.global
280-
}
281-
return localizeDeclValue(valueNode, subContext)
282-
})
283-
decl.value = Tokenizer.stringifyValues(newValuesNode)
284-
}
285-
286-
function localizeDecl(decl, context) {
287-
var valuesNode = Tokenizer.parseValues(decl.value)
288-
289-
var isAnimation = /animation?$/.test(decl.prop)
290-
if (isAnimation)
291-
return localizeAnimationShorthandDeclValues(valuesNode, decl, context)
292-
293-
var isAnimationName = /animation(-name)?$/.test(decl.prop)
294-
if (isAnimationName)
295-
return localizeDeclValues(true, valuesNode, decl, context)
296-
297-
return localizeDeclValues(false, valuesNode, decl, context)
298-
}
299-
300162
module.exports = postcss.plugin(
301163
'postcss-modules-local-by-default',
302-
(options = {}) => (css, result) => {
164+
(options = {}) => css => {
303165
if (
304166
options.mode &&
305167
options.mode !== 'global' &&
@@ -312,33 +174,6 @@ module.exports = postcss.plugin(
312174
}
313175
var pureMode = options.mode === 'pure'
314176
var globalMode = options.mode === 'global'
315-
css.walkAtRules(function(atrule) {
316-
if (/keyframes$/.test(atrule.name)) {
317-
var globalMatch = /^\s*:global\s*\((.+)\)\s*$/.exec(atrule.params)
318-
var localMatch = /^\s*:local\s*\((.+)\)\s*$/.exec(atrule.params)
319-
var globalKeyframes = globalMode
320-
if (globalMatch) {
321-
if (pureMode) {
322-
throw atrule.error(
323-
'@keyframes :global(...) is not allowed in pure mode'
324-
)
325-
}
326-
atrule.params = globalMatch[1]
327-
globalKeyframes = true
328-
} else if (localMatch) {
329-
atrule.params = localMatch[0]
330-
globalKeyframes = false
331-
} else if (!globalMode) {
332-
atrule.params = ':local(' + atrule.params + ')'
333-
}
334-
atrule.walkDecls(function(decl) {
335-
localizeDecl(decl, {
336-
options: options,
337-
global: globalKeyframes
338-
})
339-
})
340-
}
341-
})
342177
css.walkRules(function(rule) {
343178
if (
344179
rule.parent.type === 'atrule' &&
@@ -367,12 +202,6 @@ module.exports = postcss.plugin(
367202
'(pure selectors must contain at least one local class or id)'
368203
)
369204
}
370-
// Less-syntax mixins parse as rules with no nodes
371-
if (rule.nodes) {
372-
rule.nodes.forEach(function(decl) {
373-
localizeDecl(decl, context)
374-
})
375-
}
376205
rule.selector = Tokenizer.stringify(newSelector)
377206
})
378207
}

test.js

Lines changed: 9 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
const postcss = require('postcss')
2-
const plugin = require('./src')
1+
/* eslint-env jest */
2+
import postcss from 'postcss'
3+
import plugin from './src'
34

45
const tests = [
56
{
@@ -132,76 +133,6 @@ const tests = [
132133
input: '.foo :global .bar :local .foobar :local .barfoo {}',
133134
expected: ':local(.foo) .bar :local(.foobar) :local(.barfoo) {}'
134135
},
135-
{
136-
should: 'localize a single animation-name',
137-
input: '.foo { animation-name: bar; }',
138-
expected: ':local(.foo) { animation-name: :local(bar); }'
139-
},
140-
{
141-
should: 'not localize a single animation-delay',
142-
input: '.foo { animation-delay: 1s; }',
143-
expected: ':local(.foo) { animation-delay: 1s; }'
144-
},
145-
{
146-
should: 'localize multiple animation-names',
147-
input: '.foo { animation-name: bar, foobar; }',
148-
expected: ':local(.foo) { animation-name: :local(bar), :local(foobar); }'
149-
},
150-
{
151-
should: 'localize animation',
152-
input: '.foo { animation: bar 5s, foobar; }',
153-
expected: ':local(.foo) { animation: :local(bar) 5s, :local(foobar); }'
154-
},
155-
{
156-
should: 'localize animation with vendor prefix',
157-
input: '.foo { -webkit-animation: bar; animation: bar; }',
158-
expected: ':local(.foo) { -webkit-animation: :local(bar); animation: :local(bar); }'
159-
},
160-
{
161-
should: 'not localize other rules',
162-
input: '.foo { content: "animation: bar;" }',
163-
expected: ':local(.foo) { content: "animation: bar;" }'
164-
},
165-
{
166-
should: 'not localize global rules',
167-
input: ':global .foo { animation: foo; animation-name: bar; }',
168-
expected: '.foo { animation: foo; animation-name: bar; }'
169-
},
170-
{
171-
should: 'handle a complex animation rule',
172-
input: '.foo { animation: foo, bar 5s linear 2s infinite alternate, barfoo 1s; }',
173-
expected: ':local(.foo) { animation: :local(foo), :local(bar) 5s linear 2s infinite alternate, :local(barfoo) 1s; }'
174-
},
175-
{
176-
should: 'handle animations where the first value is not the animation name',
177-
input: '.foo { animation: 1s foo; }',
178-
expected: ':local(.foo) { animation: 1s :local(foo); }'
179-
},
180-
{
181-
should: 'handle animations where the first value is not the animation name whilst also using keywords',
182-
input: '.foo { animation: 1s normal ease-out infinite foo; }',
183-
expected: ':local(.foo) { animation: 1s normal ease-out infinite :local(foo); }'
184-
},
185-
{
186-
should: 'handle animations with custom timing functions',
187-
input: '.foo { animation: 1s normal cubic-bezier(0.25, 0.5, 0.5. 0.75) foo; }',
188-
expected: ':local(.foo) { animation: 1s normal cubic-bezier(0.25, 0.5, 0.5. 0.75) :local(foo); }'
189-
},
190-
{
191-
should: 'handle animations whose names are keywords',
192-
input: '.foo { animation: 1s infinite infinite; }',
193-
expected: ':local(.foo) { animation: 1s infinite :local(infinite); }'
194-
},
195-
{
196-
should: 'handle not localize an animation shorthand value of "inherit"',
197-
input: '.foo { animation: inherit; }',
198-
expected: ':local(.foo) { animation: inherit; }'
199-
},
200-
{
201-
should: 'handle "constructor" as animation name',
202-
input: '.foo { animation: constructor constructor; }',
203-
expected: ':local(.foo) { animation: :local(constructor) :local(constructor); }'
204-
},
205136
{
206137
should: 'default to global when mode provided',
207138
input: '.foo {}',
@@ -242,22 +173,6 @@ const tests = [
242173
':local(.a) .b {}'
243174
].join('\n')
244175
},
245-
{
246-
should: 'localize keyframes',
247-
input: '@keyframes foo { from { color: red; } to { color: blue; } }',
248-
expected: '@keyframes :local(foo) { from { color: red; } to { color: blue; } }'
249-
},
250-
{
251-
should: 'localize keyframes in global default mode',
252-
input: '@keyframes foo {}',
253-
options: { mode: 'global' },
254-
expected: '@keyframes foo {}'
255-
},
256-
{
257-
should: 'localize explicit keyframes',
258-
input: '@keyframes :local(foo) { 0% { color: red; } 33.3% { color: yellow; } 100% { color: blue; } } @-webkit-keyframes :global(bar) { from { color: red; } to { color: blue; } }',
259-
expected: '@keyframes :local(foo) { 0% { color: red; } 33.3% { color: yellow; } 100% { color: blue; } } @-webkit-keyframes bar { from { color: red; } to { color: blue; } }'
260-
},
261176
{
262177
should: 'ignore :export statements',
263178
input: ':export { foo: __foo; }',
@@ -356,12 +271,6 @@ const tests = [
356271
options: { mode: 'pure' },
357272
error: /"\[type="radio"\]" is not pure/
358273
},
359-
{
360-
should: 'throw on not pure keyframes',
361-
input: '@keyframes :global(foo) {}',
362-
options: { mode: 'pure' },
363-
error: /@keyframes :global\(\.\.\.\) is not allowed in pure mode/
364-
},
365274
{
366275
should: 'pass through global element',
367276
input: 'input {}',
@@ -384,7 +293,7 @@ const tests = [
384293
},
385294
{
386295
should: 'not crash on a rule without nodes',
387-
input: (function() {
296+
input: (() => {
388297
var inner = postcss.rule({ selector: '.b', ruleWithoutBody: true })
389298
var outer = postcss.rule({ selector: '.a' }).push(inner)
390299
var root = postcss.root().push(outer)
@@ -393,6 +302,11 @@ const tests = [
393302
})(),
394303
// postcss-less's stringify would honor `ruleWithoutBody` and omit the trailing `{}`
395304
expected: ':local(.a) {\n :local(.b) {}\n}'
305+
},
306+
{
307+
should: 'not localize keyframes rules',
308+
input: '@keyframes foo { from {} to {} }',
309+
expected: '@keyframes foo { from {} to {} }'
396310
}
397311
]
398312

0 commit comments

Comments
 (0)