|
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' |
| 4 | + |
| 5 | +const plugin = 'postcss-modules-local-by-default' |
3 | 6 |
|
4 | 7 | function normalizeNodeArray(nodes) {
|
5 | 8 | var array = []
|
@@ -158,222 +161,44 @@ function localizeNode(node, context) {
|
158 | 161 | return node
|
159 | 162 | }
|
160 | 163 |
|
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 |
| 164 | +module.exports = postcss.plugin(plugin, (options = {}) => css => { |
| 165 | + if ( |
| 166 | + options.mode && |
| 167 | + options.mode !== 'global' && |
| 168 | + options.mode !== 'local' && |
| 169 | + options.mode !== 'pure' |
| 170 | + ) { |
| 171 | + throw Error( |
| 172 | + 'options.mode must be either "global", "local" or "pure" (default "local")' |
| 173 | + ) |
228 | 174 | }
|
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 |
| - } |
| 175 | + var pureMode = options.mode === 'pure' |
| 176 | + var globalMode = options.mode === 'global' |
| 177 | + css.walkRules(function(rule) { |
| 178 | + if (rule.parent.type === 'atrule' && /keyframes$/.test(rule.parent.name)) { |
| 179 | + // ignore keyframe rules |
| 180 | + return |
249 | 181 | }
|
250 |
| - |
251 |
| - var subContext = { |
252 |
| - options: context.options, |
253 |
| - global: context.global, |
254 |
| - localizeNextItem: shouldParseAnimationName && !context.global |
| 182 | + var selector = Tokenizer.parse(rule.selector) |
| 183 | + var context = { |
| 184 | + options: options, |
| 185 | + global: globalMode, |
| 186 | + hasPureGlobals: false |
255 | 187 | }
|
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 |
| 188 | + var newSelector |
| 189 | + try { |
| 190 | + newSelector = localizeNode(selector, context) |
| 191 | + } catch (e) { |
| 192 | + throw rule.error(e.message) |
280 | 193 | }
|
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 |
| - |
300 |
| -module.exports = postcss.plugin( |
301 |
| - 'postcss-modules-local-by-default', |
302 |
| - (options = {}) => (css, result) => { |
303 |
| - if ( |
304 |
| - options.mode && |
305 |
| - options.mode !== 'global' && |
306 |
| - options.mode !== 'local' && |
307 |
| - options.mode !== 'pure' |
308 |
| - ) { |
309 |
| - throw Error( |
310 |
| - 'options.mode must be either "global", "local" or "pure" (default "local")' |
| 194 | + if (pureMode && context.hasPureGlobals) { |
| 195 | + throw rule.error( |
| 196 | + 'Selector "' + |
| 197 | + Tokenizer.stringify(selector) + |
| 198 | + '" is not pure ' + |
| 199 | + '(pure selectors must contain at least one local class or id)' |
311 | 200 | )
|
312 | 201 | }
|
313 |
| - var pureMode = options.mode === 'pure' |
314 |
| - 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 |
| - }) |
342 |
| - css.walkRules(function(rule) { |
343 |
| - if ( |
344 |
| - rule.parent.type === 'atrule' && |
345 |
| - /keyframes$/.test(rule.parent.name) |
346 |
| - ) { |
347 |
| - // ignore keyframe rules |
348 |
| - return |
349 |
| - } |
350 |
| - var selector = Tokenizer.parse(rule.selector) |
351 |
| - var context = { |
352 |
| - options: options, |
353 |
| - global: globalMode, |
354 |
| - hasPureGlobals: false |
355 |
| - } |
356 |
| - var newSelector |
357 |
| - try { |
358 |
| - newSelector = localizeNode(selector, context) |
359 |
| - } catch (e) { |
360 |
| - throw rule.error(e.message) |
361 |
| - } |
362 |
| - if (pureMode && context.hasPureGlobals) { |
363 |
| - throw rule.error( |
364 |
| - 'Selector "' + |
365 |
| - Tokenizer.stringify(selector) + |
366 |
| - '" is not pure ' + |
367 |
| - '(pure selectors must contain at least one local class or id)' |
368 |
| - ) |
369 |
| - } |
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 |
| - } |
376 |
| - rule.selector = Tokenizer.stringify(newSelector) |
377 |
| - }) |
378 |
| - } |
379 |
| -) |
| 202 | + rule.selector = Tokenizer.stringify(newSelector) |
| 203 | + }) |
| 204 | +}) |
0 commit comments